Ticket #11709: 0001-Added-new-performance-test-for-OsmPrimitive-tag-hand.patch

File 0001-Added-new-performance-test-for-OsmPrimitive-tag-hand.patch, 17.2 KB (added by michael2402, 9 years ago)
  • new file test/performance/org/openstreetmap/josm/PerformanceTestUtils.java

    From 03c8000a59a7a7b2a608ad4ef598f37120340eba Mon Sep 17 00:00:00 2001
    From: Michael Zangl <michael.zangl@student.kit.edu>
    Date: Mon, 20 Jul 2015 13:04:02 +0200
    Subject: [PATCH 1/4] Added new performance test for OsmPrimitive tag handling.
    
    ---
     .../openstreetmap/josm/PerformanceTestUtils.java   |  44 +++++
     .../josm/data/osm/KeyValuePerformanceTest.java     | 204 ++++++++++++++++++++
     .../josm/data/osm/OsmDataGenerator.java            | 214 +++++++++++++++++++++
     3 files changed, 462 insertions(+)
     create mode 100644 test/performance/org/openstreetmap/josm/PerformanceTestUtils.java
     create mode 100644 test/performance/org/openstreetmap/josm/data/osm/KeyValuePerformanceTest.java
     create mode 100644 test/performance/org/openstreetmap/josm/data/osm/OsmDataGenerator.java
    
    diff --git a/test/performance/org/openstreetmap/josm/PerformanceTestUtils.java b/test/performance/org/openstreetmap/josm/PerformanceTestUtils.java
    new file mode 100644
    index 0000000..f2d64ce
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm;
     3
     4/**
     5 * Timer utilities for performance tests.
     6 * @author Michael Zangl
     7 */
     8public class PerformanceTestUtils {
     9    /**
     10     * A timer that measures the time from it's creation to the {@link #done()} call.
     11    * @author Michael Zangl
     12     */
     13    public static class PerformanceTestTimer {
     14        private String name;
     15        private long time;
     16
     17        protected PerformanceTestTimer(String name) {
     18            this.name = name;
     19            time = System.nanoTime();
     20        }
     21
     22        /**
     23         * Prints the time since this timer was created.
     24         */
     25        public void done() {
     26            long dTime = System.nanoTime() - time;
     27            System.out.println("TIMER " + name + ": " + dTime / 1000000 + "ms");
     28        }
     29    }
     30
     31    private PerformanceTestUtils() {
     32    }
     33
     34    /**
     35     * Starts a new performance timer.
     36     * @param name The name/description of the timer.
     37     * @return A {@link PerformanceTestTimer} object of which you can call {@link PerformanceTestTimer#done()} when done.
     38     */
     39    public static PerformanceTestTimer startTimer(String name) {
     40        System.gc();
     41        System.runFinalization();
     42        return new PerformanceTestTimer(name);
     43    }
     44}
  • new file test/performance/org/openstreetmap/josm/data/osm/KeyValuePerformanceTest.java

    diff --git a/test/performance/org/openstreetmap/josm/data/osm/KeyValuePerformanceTest.java b/test/performance/org/openstreetmap/josm/data/osm/KeyValuePerformanceTest.java
    new file mode 100644
    index 0000000..83171b5
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.osm;
     3
     4import static org.junit.Assert.assertFalse;
     5import static org.junit.Assert.assertTrue;
     6
     7import java.util.ArrayList;
     8import java.util.Random;
     9
     10import org.apache.commons.lang.RandomStringUtils;
     11import org.junit.Before;
     12import org.junit.BeforeClass;
     13import org.junit.Test;
     14import org.openstreetmap.josm.JOSMFixture;
     15import org.openstreetmap.josm.PerformanceTestUtils;
     16import org.openstreetmap.josm.PerformanceTestUtils.PerformanceTestTimer;
     17import org.openstreetmap.josm.data.osm.OsmDataGenerator.KeyValueDataGenerator;
     18
     19/**
     20 * This test measures the performance of {@link OsmPrimitive#get(String)} and related.
     21 * @author Michael Zangl
     22 */
     23public class KeyValuePerformanceTest {
     24    private static final int PUT_RUNS = 10000;
     25    private static final int GET_RUNS = 100000;
     26    private static final int TEST_STRING_COUNT = 10000;
     27    private static final int STRING_INTERN_TESTS = 5000000;
     28    private static final double[] TAG_NODE_RATIOS = new double[] { .05, .3, 3, 20, 200 };
     29    private ArrayList<String> testStrings = new ArrayList<>();
     30    private Random random;
     31
     32    /**
     33     * Prepare the test.
     34     */
     35    @BeforeClass
     36    public static void createJOSMFixture() {
     37        JOSMFixture.createPerformanceTestFixture().init(true);
     38    }
     39
     40    /**
     41     * See if there is a big difference between Strings that are interned and those that are not.
     42     */
     43    @Test
     44    public void meassureStringEqualsIntern() {
     45        String str1Interned = "string1";
     46        String str1InternedB = "string1";
     47        String str1 = new String(str1Interned);
     48        String str1B = new String(str1Interned);
     49        String str2Interned = "string2";
     50        String str2 = new String(str2Interned);
     51
     52        for (int i = 0; i < STRING_INTERN_TESTS; i++) {
     53            // warm up
     54            assertTrue(str1.equals(str1B));
     55        }
     56
     57        PerformanceTestTimer timer = PerformanceTestUtils.startTimer("Assertion overhead.");
     58        for (int i = 0; i < STRING_INTERN_TESTS; i++) {
     59            assertTrue(true);
     60        }
     61        timer.done();
     62
     63        timer = PerformanceTestUtils.startTimer("str1.equals(str2) succeeds (without intern)");
     64        for (int i = 0; i < STRING_INTERN_TESTS; i++) {
     65            assertTrue(str1.equals(str1B));
     66        }
     67        timer.done();
     68
     69        timer = PerformanceTestUtils.startTimer("str1 == str2 succeeds");
     70        for (int i = 0; i < STRING_INTERN_TESTS; i++) {
     71            assertTrue(str1Interned == str1InternedB);
     72        }
     73        timer.done();
     74
     75        timer = PerformanceTestUtils.startTimer("str1 == str2.intern() succeeds");
     76        for (int i = 0; i < STRING_INTERN_TESTS; i++) {
     77            assertTrue(str1Interned == str1.intern());
     78        }
     79        timer.done();
     80
     81        timer = PerformanceTestUtils.startTimer("str1 == str2.intern() succeeds for interned string");
     82        for (int i = 0; i < STRING_INTERN_TESTS; i++) {
     83            assertTrue(str1Interned == str1InternedB.intern());
     84        }
     85        timer.done();
     86
     87        timer = PerformanceTestUtils.startTimer("str1.equals(str2) = fails (without intern)");
     88        for (int i = 0; i < STRING_INTERN_TESTS; i++) {
     89            assertFalse(str1.equals(str2));
     90        }
     91        timer.done();
     92
     93        timer = PerformanceTestUtils.startTimer("str1 == str2 fails");
     94        for (int i = 0; i < STRING_INTERN_TESTS; i++) {
     95            assertFalse(str1Interned == str2Interned);
     96        }
     97        timer.done();
     98
     99        timer = PerformanceTestUtils.startTimer("str1 == str2.intern() fails");
     100        for (int i = 0; i < STRING_INTERN_TESTS; i++) {
     101            assertFalse(str1Interned == str2.intern());
     102        }
     103        timer.done();
     104    }
     105
     106    /**
     107     * Generate an array of test strings.
     108     */
     109    @Before
     110    public void generateTestStrings() {
     111        testStrings.clear();
     112        random = new Random(123);
     113        for (int i = 0; i < TEST_STRING_COUNT; i++) {
     114            testStrings.add(RandomStringUtils.random(10, 0, 0, true, true, null, random));
     115        }
     116    }
     117
     118    /**
     119     * Measure the speed of {@link OsmPrimitive#put(String, String)}
     120     */
     121    @Test
     122    public void testKeyValuePut() {
     123        for (double tagNodeRatio : TAG_NODE_RATIOS) {
     124            int nodeCount = (int) (PUT_RUNS / tagNodeRatio);
     125            KeyValueDataGenerator generator = OsmDataGenerator.getKeyValue(nodeCount, 0);
     126            generator.generateDataSet();
     127
     128            PerformanceTestTimer timer = PerformanceTestUtils
     129                    .startTimer("OsmPrimitive#put(String, String) with put/node ratio " + tagNodeRatio);
     130
     131            for (int i = 0; i < PUT_RUNS; i++) {
     132                String key = generator.randomKey();
     133                String value = generator.randomValue();
     134                generator.randomNode().put(key, value);
     135            }
     136
     137            timer.done();
     138        }
     139    }
     140
     141    /**
     142     * Measure the speed of {@link OsmPrimitive#get(String)}
     143     */
     144    @Test
     145    public void testKeyValueGet() {
     146        for (double tagNodeRatio : TAG_NODE_RATIOS) {
     147            KeyValueDataGenerator generator = OsmDataGenerator.getKeyValue(tagNodeRatio);
     148            generator.generateDataSet();
     149
     150            PerformanceTestTimer timer = PerformanceTestUtils
     151                    .startTimer("OsmPrimitive#get(String) with tag/node ratio " + tagNodeRatio);
     152            for (int i = 0; i < GET_RUNS; i++) {
     153                String key = generator.randomKey();
     154                // to make comparison easier.
     155                generator.randomValue();
     156                generator.randomNode().get(key);
     157            }
     158            timer.done();
     159        }
     160    }
     161
     162    /**
     163     * Measure the speed of {@link OsmPrimitive#getKeys()}
     164     */
     165    @Test
     166    public void testKeyValueGetKeys() {
     167        for (double tagNodeRatio : TAG_NODE_RATIOS) {
     168            KeyValueDataGenerator generator = OsmDataGenerator.getKeyValue(tagNodeRatio);
     169            generator.generateDataSet();
     170
     171            PerformanceTestTimer timer = PerformanceTestUtils.startTimer("OsmPrimitive#getKeys() with tag/node ratio "
     172                    + tagNodeRatio);
     173
     174            for (int i = 0; i < GET_RUNS; i++) {
     175                // to make comparison easier.
     176                generator.randomKey();
     177                generator.randomValue();
     178                generator.randomNode().getKeys();
     179            }
     180            timer.done();
     181        }
     182    }
     183
     184    /**
     185     * Measure the speed of {@link OsmPrimitive#getKeys()}.get(key)
     186     */
     187    @Test
     188    public void testKeyValueGetKeysGet() {
     189        for (double tagNodeRatio : TAG_NODE_RATIOS) {
     190            KeyValueDataGenerator generator = OsmDataGenerator.getKeyValue(tagNodeRatio);
     191            generator.generateDataSet();
     192
     193            PerformanceTestTimer timer = PerformanceTestUtils
     194                    .startTimer("OsmPrimitive#getKeys().get(key) with tag/node ratio " + tagNodeRatio);
     195            for (int i = 0; i < GET_RUNS; i++) {
     196                String key = generator.randomKey();
     197                // to make comparison easier.
     198                generator.randomValue();
     199                generator.randomNode().getKeys().get(key);
     200            }
     201            timer.done();
     202        }
     203    }
     204}
  • new file test/performance/org/openstreetmap/josm/data/osm/OsmDataGenerator.java

    diff --git a/test/performance/org/openstreetmap/josm/data/osm/OsmDataGenerator.java b/test/performance/org/openstreetmap/josm/data/osm/OsmDataGenerator.java
    new file mode 100644
    index 0000000..fce9c2d
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.osm;
     3
     4import java.io.File;
     5import java.util.ArrayList;
     6import java.util.Random;
     7
     8import org.apache.commons.lang.RandomStringUtils;
     9import org.openstreetmap.josm.data.coor.EastNorth;
     10import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     11
     12/**
     13 * This is an utility class that allows you to generate OSM test data.
     14 * @author Michael Zangl
     15 */
     16public class OsmDataGenerator {
     17    private static final int DEFAULT_KEY_VALUE_RATIO = 3;
     18    private static final int DEFAULT_NODE_COUNT = 1000;
     19    private static final String DATA_DIR = "data_nodist" + File.separator + "osmfiles";
     20
     21    /**
     22     * A generator that generates test data by filling a data set.
     23     * @author Michael Zangl
     24     */
     25    public static abstract class DataGenerator {
     26        private String datasetName;
     27        protected final Random random;
     28        private DataSet ds;
     29
     30        /**
     31         * Create a new generator.
     32         * @param datasetName The name for the generator. Only used for human readability.
     33         */
     34        protected DataGenerator(String datasetName) {
     35            this.datasetName = datasetName;
     36            this.random = new Random(1234);
     37        }
     38
     39        /**
     40         * Generates the data set. If this method is called twice, the same dataset is returned.
     41         * @return The generated data set.
     42         */
     43        public DataSet generateDataSet() {
     44            ensureInitialized();
     45            return ds;
     46        }
     47
     48        protected void ensureInitialized() {
     49            if (ds == null) {
     50                ds = new DataSet();
     51                fillData(ds);
     52            }
     53        }
     54
     55        protected abstract void fillData(DataSet ds);
     56
     57        /**
     58         * Create a random node and add it to the dataset.
     59         * @return
     60         */
     61        protected Node createRandomNode(DataSet ds) {
     62            Node node = new Node();
     63            node.setEastNorth(new EastNorth(random.nextDouble(), random.nextDouble()));
     64            ds.addPrimitive(node);
     65            return node;
     66        }
     67
     68        protected String randomString() {
     69            return RandomStringUtils.random(12, 0, 0, true, true, null, random);
     70        }
     71
     72        /**
     73         * Gets a file path where this data could be stored.
     74         * @return A file path.
     75         */
     76        public File getFile() {
     77            return new File(DATA_DIR + File.separator + datasetName + ".osm");
     78        }
     79
     80        /**
     81         * Creates a new {@link OsmDataLayer} that uses the underlying dataset of this generator.
     82         * @return A new data layer.
     83         */
     84        public OsmDataLayer createDataLayer() {
     85            return new OsmDataLayer(generateDataSet(), datasetName, getFile());
     86        }
     87
     88        @Override
     89        public String toString() {
     90            return "DataGenerator [datasetName=" + datasetName + "]";
     91        }
     92    }
     93
     94    /**
     95     * A data generator that generates a bunch of random nodes.
     96    * @author Michael Zangl
     97     */
     98    public static class NodeDataGenerator extends DataGenerator {
     99        protected final ArrayList<Node> nodes = new ArrayList<>();
     100        private final int nodeCount;
     101
     102        private NodeDataGenerator(String datasetName, int nodeCount) {
     103            super(datasetName);
     104            this.nodeCount = nodeCount;
     105        }
     106
     107        @Override
     108        public void fillData(DataSet ds) {
     109            for (int i = 0; i < nodeCount; i++) {
     110                nodes.add(createRandomNode(ds));
     111            }
     112        }
     113
     114        /**
     115         * Gets a random node of this dataset.
     116         * @return A random node.
     117         */
     118        public Node randomNode() {
     119            ensureInitialized();
     120            return nodes.get(random.nextInt(nodes.size()));
     121        }
     122    }
     123
     124    /**
     125     * A data generator that generates a bunch of random nodes and fills them with keys/values.
     126    * @author Michael Zangl
     127     */
     128    public static class KeyValueDataGenerator extends NodeDataGenerator {
     129
     130        private static final int VALUE_COUNT = 200;
     131        private static final int KEY_COUNT = 150;
     132        private final double tagNodeRation;
     133        private ArrayList<String> keys;
     134        private ArrayList<String> values;
     135
     136        private KeyValueDataGenerator(String datasetName, int nodeCount, double tagNodeRation) {
     137            super(datasetName, nodeCount);
     138            this.tagNodeRation = tagNodeRation;
     139        }
     140
     141        @Override
     142        public void fillData(DataSet ds) {
     143            super.fillData(ds);
     144            keys = new ArrayList<>();
     145            for (int i = 0; i < KEY_COUNT; i++) {
     146                keys.add(randomString());
     147            }
     148            values = new ArrayList<>();
     149            for (int i = 0; i < VALUE_COUNT; i++) {
     150                values.add(randomString());
     151            }
     152
     153            double tags = nodes.size() * tagNodeRation;
     154            for (int i = 0; i < tags; i++) {
     155                String key = randomKey();
     156                String value = randomValue();
     157                nodes.get(random.nextInt(nodes.size())).put(key, value);
     158            }
     159        }
     160
     161        /**
     162         * Gets a random value that was used to fill the tags.
     163         * @return A random String probably used in as value somewhere.
     164         */
     165        public String randomValue() {
     166            ensureInitialized();
     167            return values.get(random.nextInt(values.size()));
     168        }
     169
     170        /**
     171         * Gets a random key that was used to fill the tags.
     172         * @return A random String probably used in as key somewhere.
     173         */
     174        public String randomKey() {
     175            ensureInitialized();
     176            return keys.get(random.nextInt(keys.size()));
     177        }
     178    }
     179
     180    /**
     181     * Generate a generator that creates some nodes and adds random keys and values to it.
     182     * @return The generator
     183     */
     184    public static KeyValueDataGenerator getKeyValue() {
     185        return getKeyValue(DEFAULT_KEY_VALUE_RATIO);
     186    }
     187
     188    /**
     189     * Generate a generator that creates some nodes and adds random keys and values to it.
     190     * @param tagNodeRation How many tags to add per node (on average).
     191     * @return The generator
     192     */
     193    public static KeyValueDataGenerator getKeyValue(double tagNodeRation) {
     194        return getKeyValue(DEFAULT_NODE_COUNT, tagNodeRation);
     195    }
     196
     197    /**
     198     * Generate a generator that creates some nodes and adds random keys and values to it.
     199     * @param nodeCount The number of nodes the dataset should contain.
     200     * @param tagNodeRation How many tags to add per node (on average).
     201     * @return The generator
     202     */
     203    public static KeyValueDataGenerator getKeyValue(int nodeCount, double tagNodeRation) {
     204        return new KeyValueDataGenerator("key-value", nodeCount, tagNodeRation);
     205    }
     206
     207    /**
     208     * Create a generator that generates a bunch of nodes.
     209     * @return The generator
     210     */
     211    public static DataGenerator getNodes() {
     212        return new NodeDataGenerator("nodes", DEFAULT_NODE_COUNT);
     213    }
     214}