blob: d613565a5fb1f2dd3e05237555a7848ed6e2e5c0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sis.feature;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import org.opengis.feature.Feature;
import org.apache.sis.internal.util.StandardDateFormat;
import static java.util.Collections.singletonMap;
/**
* Compares {@link org.apache.sis.feature} memory usage with a plain {@link HashMap}.
* This class simulates creation of features having the following properties:
*
* <ul>
* <li>{@code "city"} : {@link String}</li>
* <li>{@code "longitude"} : {@link Float}</li>
* <li>{@code "latitude"} : {@link Float}</li>
* </ul>
*/
public final class FeatureMemoryBenchmark {
/**
* Features created by the benchmark. We need to keep reference to all of them
* for preventing the garbage collector to free memory.
*/
private final List<Object> features;
/**
* If we use SIS implementation, the feature type. Otherwise {@code null}.
*/
private final DefaultFeatureType type;
/**
* In the case of non-SIS implementation, whether we use simple features or complex features.
*/
private final boolean isSimple;
/**
* Random number generator for feature data.
*/
private final Random random;
/**
* A buffer for generating random strings.
*/
private final char[] buffer;
/**
* Creates a new benchmark.
*
* @param useSIS {@code true} for using SIS implementation, or {@code false} for {@link HashMap}.
* @param isSimple in the case of non-SIS implementation, whether we use simple features or complex features.
*/
private FeatureMemoryBenchmark(final boolean useSIS, final boolean isSimple) {
features = new ArrayList<>(100000);
this.isSimple = isSimple;
if (useSIS) {
type = new DefaultFeatureType (singletonMap("name", "City"), false, null,
new DefaultAttributeType<>(singletonMap("name", "city"), String.class, 1, 1, null),
new DefaultAttributeType<>(singletonMap("name", "longitude"), Float.class, 1, 1, null),
new DefaultAttributeType<>(singletonMap("name", "latitude"), Float.class, 1, 1, null));
} else {
type = null;
}
random = new Random();
buffer = new char[8];
}
/**
* The old feature implementation.
*/
private static final class SimpleFeature {
final HashMap<String, Object> attributes = new HashMap<>(8);
}
/**
* A more complete feature implementation.
*/
private static final class ComplexFeature {
final HashMap<String, List<Property>> properties = new HashMap<>(8);
}
/**
* The property to be stored in {@link ComplexFeature}.
*/
private static final class Property {
final Object value;
private Property(final Object value) {
this.value = value;
}
static List<Property> asList(final Object value) {
final List<Property> list = new ArrayList<>(2);
list.add(new Property(value));
return list;
}
}
/**
* Creates a new feature instance with random data.
*/
private Object createFeature() {
for (int i=0; i<buffer.length; i++) {
buffer[i] = (char) ('A' + random.nextInt(26));
}
final String city = new String(buffer);
final Float latitude = random.nextFloat() * 180 - 90;
final Float longitude = random.nextFloat() * 360 - 180;
if (type != null) {
final Feature feature = type.newInstance();
feature.setPropertyValue("city", city);
feature.setPropertyValue("latitude", latitude);
feature.setPropertyValue("longitude", longitude);
return feature;
} else if (isSimple) {
final SimpleFeature feature = new SimpleFeature();
feature.attributes.put("city", city);
feature.attributes.put("latitude", latitude);
feature.attributes.put("longitude", longitude);
return feature;
} else {
final ComplexFeature feature = new ComplexFeature();
feature.properties.put("city", Property.asList(city));
feature.properties.put("latitude", Property.asList(latitude));
feature.properties.put("longitude", Property.asList(longitude));
return feature;
}
}
/**
* Creates a bunch of features until we get out of memory.
*/
private void run() {
for (int i=0; i<10000000; i++) {
final Object feature;
try {
feature = createFeature();
} catch (OutOfMemoryError e) {
final int n = features.size();
features.clear();
System.gc();
System.console().printf("Feature count: %d%n", n);
return;
}
features.add(feature);
}
}
/**
* Runs from the command line. This method expect one argument, which is "sis", "simple" or "complex".
*
* @param arguments command line arguments.
*/
public static void main(final String[] arguments) {
if (arguments.length == 1) {
final String arg = arguments[0];
final boolean useSIS = arg.equalsIgnoreCase("sis");
boolean isSimple = false;
if (useSIS || (isSimple = arg.equalsIgnoreCase("simple")) || arg.equalsIgnoreCase("complex")) {
final FeatureMemoryBenchmark b = new FeatureMemoryBenchmark(useSIS, isSimple);
long time = System.nanoTime();
b.run();
time = System.nanoTime() - time;
System.console().printf("Ellapsed time: %f%n", time / (float) StandardDateFormat.NANOS_PER_SECOND);
return;
}
}
System.console().printf("Expected argument: 'sis', 'simple' or 'complex'.%n");
}
}