// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.data.osm; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Random; import org.apache.commons.lang.RandomStringUtils; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; import org.openstreetmap.josm.JOSMFixture; import org.openstreetmap.josm.PerformanceTestUtils; import org.openstreetmap.josm.PerformanceTestUtils.PerformanceTestTimer; import org.openstreetmap.josm.data.osm.OsmDataGenerator.KeyValueDataGenerator; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * This test measures the performance of {@link OsmPrimitive#get(String)} and related. * @author Michael Zangl */ public class KeyValuePerformanceTest { private static final int PUT_RUNS = 10000; private static final int GET_RUNS = 100000; private static final int TEST_STRING_COUNT = 10000; private static final int STRING_INTERN_TESTS = 5000000; private static final double[] TAG_NODE_RATIOS = new double[] {.05, .3, 3, 20, 200}; private ArrayList<String> testStrings = new ArrayList<>(); private Random random; /** * Global timeout applied to all test methods. */ @Rule @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") public Timeout globalTimeout = Timeout.seconds(15*60); /** * Prepare the test. */ @BeforeClass public static void createJOSMFixture() { JOSMFixture.createPerformanceTestFixture().init(true); } /** * See if there is a big difference between Strings that are interned and those that are not. */ @Test @SuppressFBWarnings(value = "DM_STRING_CTOR", justification = "test Strings that are interned and those that are not") public void testMeasureStringEqualsIntern() { String str1Interned = "string1"; String str1InternedB = "string1"; String str1 = new String(str1Interned); String str1B = new String(str1Interned); String str2Interned = "string2"; String str2 = new String(str2Interned); for (int i = 0; i < STRING_INTERN_TESTS; i++) { // warm up assertEquals(str1, str1B); } PerformanceTestTimer timer = PerformanceTestUtils.startTimer("Assertion overhead."); for (int i = 0; i < STRING_INTERN_TESTS; i++) { assertTrue(true); } timer.done(); timer = PerformanceTestUtils.startTimer("str1.equals(str2) succeeds (without intern)"); for (int i = 0; i < STRING_INTERN_TESTS; i++) { assertEquals(str1, str1B); } timer.done(); timer = PerformanceTestUtils.startTimer("str1 == str2 succeeds"); for (int i = 0; i < STRING_INTERN_TESTS; i++) { assertSame(str1Interned, str1InternedB); } timer.done(); timer = PerformanceTestUtils.startTimer("str1 == str2.intern() succeeds"); for (int i = 0; i < STRING_INTERN_TESTS; i++) { assertSame(str1Interned, str1.intern()); } timer.done(); timer = PerformanceTestUtils.startTimer("str1 == str2.intern() succeeds for interned string"); for (int i = 0; i < STRING_INTERN_TESTS; i++) { assertSame(str1Interned, str1InternedB.intern()); } timer.done(); timer = PerformanceTestUtils.startTimer("str1.equals(str2) = fails (without intern)"); for (int i = 0; i < STRING_INTERN_TESTS; i++) { assertFalse(str1.equals(str2)); } timer.done(); timer = PerformanceTestUtils.startTimer("str1 == str2 fails"); for (int i = 0; i < STRING_INTERN_TESTS; i++) { assertNotSame(str1Interned, str2Interned); } timer.done(); timer = PerformanceTestUtils.startTimer("str1 == str2.intern() fails"); for (int i = 0; i < STRING_INTERN_TESTS; i++) { assertNotSame(str1Interned, str2.intern()); } timer.done(); } /** * Generate an array of test strings. */ @Before public void generateTestStrings() { testStrings.clear(); random = new SecureRandom(); for (int i = 0; i < TEST_STRING_COUNT; i++) { testStrings.add(RandomStringUtils.random(10, 0, 0, true, true, null, random)); } } /** * Measure the speed of {@link OsmPrimitive#put(String, String)} */ @Test public void testKeyValuePut() { for (double tagNodeRatio : TAG_NODE_RATIOS) { int nodeCount = (int) (PUT_RUNS / tagNodeRatio); KeyValueDataGenerator generator = OsmDataGenerator.getKeyValue(nodeCount, 0); generator.generateDataSet(); PerformanceTestTimer timer = PerformanceTestUtils .startTimer("OsmPrimitive#put(String, String) with put/node ratio " + tagNodeRatio); for (int i = 0; i < PUT_RUNS; i++) { String key = generator.randomKey(); String value = generator.randomValue(); generator.randomNode().put(key, value); } timer.done(); } } /** * Measure the speed of {@link OsmPrimitive#get(String)} */ @Test public void testKeyValueGet() { for (double tagNodeRatio : TAG_NODE_RATIOS) { KeyValueDataGenerator generator = OsmDataGenerator.getKeyValue(tagNodeRatio); generator.generateDataSet(); PerformanceTestTimer timer = PerformanceTestUtils .startTimer("OsmPrimitive#get(String) with tag/node ratio " + tagNodeRatio); for (int i = 0; i < GET_RUNS; i++) { String key = generator.randomKey(); // to make comparison easier. generator.randomValue(); generator.randomNode().get(key); } timer.done(); } } /** * Measure the speed of {@link OsmPrimitive#getKeys()} */ @Test public void testKeyValueGetKeys() { for (double tagNodeRatio : TAG_NODE_RATIOS) { KeyValueDataGenerator generator = OsmDataGenerator.getKeyValue(tagNodeRatio); generator.generateDataSet(); PerformanceTestTimer timer = PerformanceTestUtils.startTimer("OsmPrimitive#getKeys() with tag/node ratio " + tagNodeRatio); for (int i = 0; i < GET_RUNS; i++) { // to make comparison easier. generator.randomKey(); generator.randomValue(); generator.randomNode().getKeys(); } timer.done(); } } /** * Measure the speed of {@link OsmPrimitive#getKeys()}.get(key) */ @Test @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") public void testKeyValueGetKeysGet() { for (double tagNodeRatio : TAG_NODE_RATIOS) { KeyValueDataGenerator generator = OsmDataGenerator.getKeyValue(tagNodeRatio); generator.generateDataSet(); PerformanceTestTimer timer = PerformanceTestUtils .startTimer("OsmPrimitive#getKeys().get(key) with tag/node ratio " + tagNodeRatio); for (int i = 0; i < GET_RUNS; i++) { String key = generator.randomKey(); // to make comparison easier. generator.randomValue(); generator.randomNode().getKeys().get(key); } timer.done(); } } }