diff --git a/class-match/src/jmh/java/datadog/instrument/classmatch/InternalNamesBenchmark.java b/class-match/src/jmh/java/datadog/instrument/classmatch/InternalNamesBenchmark.java new file mode 100644 index 0000000..32003a2 --- /dev/null +++ b/class-match/src/jmh/java/datadog/instrument/classmatch/InternalNamesBenchmark.java @@ -0,0 +1,122 @@ +package datadog.instrument.classmatch; + +import static datadog.instrument.classmatch.InternalMatchers.internalName; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.openjdk.jmh.annotations.Mode.AverageTime; + +import datadog.instrument.testing.SampleClasses; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.OperationsPerInvocation; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +/** Reports time per corpus class matched against every sampled group, without short-circuiting. */ +@State(Scope.Thread) +@BenchmarkMode(AverageTime) +@OutputTimeUnit(NANOSECONDS) +@Fork(3) +@Warmup(iterations = 4, time = 1) +@Measurement(iterations = 6, time = 1) +public class InternalNamesBenchmark { + private static final int CLASS_COUNT = 33957; + + @Param({"STRING", "TYPE_STRING"}) + public String queryType; + + private InternalNames[] groups; + private CharSequence[] queries; + + @Setup + public void setup() throws IOException { + String[] classNames = + SampleClasses.loadClassNames().stream() + .map(InternalMatchers::internalName) + .toArray(String[]::new); + queries = Arrays.stream(classNames).map(this::query).toArray(CharSequence[]::new); + // OperationsPerInvocation must match the shared corpus size. + if (queries.length != CLASS_COUNT) { + throw new IllegalStateException("Update CLASS_COUNT for the shared corpus"); + } + + List sets = new ArrayList<>(); + long matches = 0; + try (InputStream in = + requireNonNull( + InternalNamesBenchmark.class.getResourceAsStream("type-name-groups.csv"), + "type-name-groups.csv"); + BufferedReader reader = new BufferedReader(new InputStreamReader(in, UTF_8))) { + for (String line; (line = reader.readLine()) != null; ) { + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + String[] columns = line.split(","); + InternalNames names = new InternalNames(Arrays.asList(columns).subList(1, columns.length)); + sets.add(names); + Set expected = new HashSet<>(); + for (int i = 1; i < columns.length; i++) { + expected.add(internalName(columns[i])); + } + // Validate hits even for group members absent from the shared corpus. + for (String name : expected) { + if (!names.containsType(query(name))) { + throw new IllegalStateException("Missing group member: " + name); + } + } + for (int i = 0; i < queries.length; i++) { + CharSequence query = queries[i]; + boolean hit = expected.contains(classNames[i]); + if (names.containsType(query) != hit) { + throw new IllegalStateException("Incorrect corpus membership: " + query); + } + matches += hit ? 1 : 0; + } + } + } + if (sets.isEmpty()) { + throw new IllegalStateException("No sampled groups"); + } + groups = sets.toArray(new InternalNames[0]); + System.out.printf( + "Corpus: %d classes, %d groups, %d hits%n", queries.length, groups.length, matches); + } + + private CharSequence query(String name) { + // Both query forms have their hashes computed outside measurement. + int hash = name.hashCode(); + if (queryType.equals("STRING")) { + return name; + } + String descriptor = "L" + name + ";"; + return new TypeString(descriptor, 1, descriptor.length() - 1, hash); + } + + @Benchmark + @OperationsPerInvocation(CLASS_COUNT) + public long matchAllGroups() { + long matches = 0; + for (CharSequence query : queries) { + for (InternalNames names : groups) { + matches += names.containsType(query) ? 1 : 0; + } + } + return matches; + } +} diff --git a/class-match/src/jmh/resources/datadog/instrument/classmatch/type-name-groups.csv b/class-match/src/jmh/resources/datadog/instrument/classmatch/type-name-groups.csv new file mode 100644 index 0000000..9a79aed --- /dev/null +++ b/class-match/src/jmh/resources/datadog/instrument/classmatch/type-name-groups.csv @@ -0,0 +1,27 @@ +# Literal dotted namedOneOf/namedNoneOf groups of two or more names from dd-trace-java. +# Computed arguments excluded; duplicate groups and argument order retained. +namedOneOf,scala.collection.Seq,scala.collection.immutable.Seq +namedOneOf,software.amazon.awssdk.core.internal.http.pipeline.stages.MakeHttpRequestStage,software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage +namedOneOf,com.hazelcast.client.impl.HazelcastClientInstanceImpl,com.hazelcast.client.impl.clientside.HazelcastClientInstanceImpl +namedOneOf,org.hibernate.Session,org.hibernate.StatelessSession +namedOneOf,org.hibernate.Session,org.hibernate.StatelessSession +namedOneOf,org.hibernate.Session,org.hibernate.StatelessSession +namedOneOf,com.netflix.hystrix.HystrixCommand,com.netflix.hystrix.HystrixObservableCommand +namedOneOf,org.apache.ignite.cache.query.Query,org.apache.ignite.cache.query.SqlFieldsQuery +namedOneOf,io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe,io.grpc.netty.shaded.io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe,io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe,io.grpc.netty.shaded.io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe,io.netty.channel.kqueue.AbstractKQueueChannel$AbstractKQueueUnsafe,io.grpc.netty.shaded.io.netty.channel.kqueue.AbstractKQueueChannel$AbstractKQueueUnsafe +namedOneOf,reactor.core.scheduler.SchedulerTask,reactor.core.scheduler.WorkerTask +namedOneOf,io.netty.util.concurrent.GlobalEventExecutor,io.grpc.netty.shaded.io.netty.util.concurrent.GlobalEventExecutor,com.couchbase.client.deps.io.netty.util.concurrent.GlobalEventExecutor +namedOneOf,io.netty.handler.timeout.IdleStateHandler,io.grpc.netty.shaded.io.netty.handler.timeout.IdleStateHandler +namedOneOf,io.netty.channel.ChannelHandlerContext,io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext +namedOneOf,org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor,org.opensearch.common.util.concurrent.OpenSearchThreadPoolExecutor +namedNoneOf,java.lang.ClassLoader,com.ibm.oti.vm.BootstrapClassLoader +namedOneOf,org.eclipse.jetty.client.api.Request,org.eclipse.jetty.client.HttpRequest +namedOneOf,org.eclipse.jetty.client.api.Request,org.eclipse.jetty.client.HttpRequest +namedOneOf,io.micronaut.http.MutableHttpResponse,io.micronaut.http.HttpResponse +namedOneOf,reactor.core.publisher.Mono,reactor.core.publisher.Flux +namedOneOf,reactor.core.publisher.Mono,reactor.core.publisher.Flux +namedOneOf,redis.Request,redis.ActorRequest,redis.BufferedRequest,redis.RoundRobinPoolRequest +namedOneOf,com.ibm.ws.session.HttpSessionFacade,org.apache.catalina.session.StandardSessionFacade +namedOneOf,com.twilio.base.Creator,com.twilio.base.Deleter,com.twilio.base.Fetcher,com.twilio.base.Reader,com.twilio.base.Updater +namedOneOf,com.twilio.base.Creator,com.twilio.base.Deleter,com.twilio.base.Fetcher,com.twilio.base.Reader,com.twilio.base.Updater +namedOneOf,io.vertx.redis.client.impl.RedisConnectionImpl,io.vertx.redis.client.impl.RedisStandaloneConnection diff --git a/class-match/src/main/java/datadog/instrument/classmatch/InternalNames.java b/class-match/src/main/java/datadog/instrument/classmatch/InternalNames.java index a26ad9f..ed5b177 100644 --- a/class-match/src/main/java/datadog/instrument/classmatch/InternalNames.java +++ b/class-match/src/main/java/datadog/instrument/classmatch/InternalNames.java @@ -14,16 +14,15 @@ import java.util.NoSuchElementException; /** - * Compact immutable hashtable of internal names, representing types to be matched against. + * Immutable set of internal names using linear probing. A parallel array of cached hashes lets + * probes skip strings whose hashes differ. * - *

The key feature of this implementation is that it supports querying by arbitrary {@link - * CharSequence}s, as long as the char sequence uses the same hash algorithm as {@link String}. + *

Queries may be any {@link CharSequence} with a {@link String}-compatible hash code. */ final class InternalNames extends AbstractSet { - private static final int MAX_HASH_ATTEMPTS = 3; - - private final String[] table; private final int slotMask; + private final int[] hashes; + private final String[] table; /** * Creates a new set of internal names containing the given types of interest. @@ -31,22 +30,25 @@ final class InternalNames extends AbstractSet { * @param types the types of interest */ InternalNames(Collection types) { - // attempt to hash types into a table with ~75% load factor - int tableSize = Math.max(8, types.size() * 4 / 3) - 1; - int slotMask = -1 >>> Integer.numberOfLeadingZeros(tableSize); - String[] table = new String[slotMask + 1]; - Iterator itr = types.iterator(); - while (itr.hasNext()) { - // add types one by one, watching out for unsolvable collisions - if (!add(table, slotMask, internalName(itr.next()))) { - // cannot add type without collision; grow table and restart additions - slotMask = (slotMask << 1) + 1; - table = new String[slotMask + 1]; - itr = types.iterator(); + // At most 50% full, leaving empty slots to terminate probes. + int tableSize = Math.max(8, types.size() * 2) - 1; + + this.slotMask = -1 >>> Integer.numberOfLeadingZeros(tableSize); + this.hashes = new int[slotMask + 1]; + this.table = new String[slotMask + 1]; + + for (String type : types) { + final String name = internalName(type); + final int h = hash(name.hashCode()); + + int hashed, slot = h & slotMask; + while ((hashed = hashes[slot]) != 0 && (hashed != h || !table[slot].equals(name))) { + slot = (slot + 1) & slotMask; } + + hashes[slot] = h; + table[slot] = name; } - this.table = table; - this.slotMask = slotMask; } /** @@ -56,47 +58,24 @@ final class InternalNames extends AbstractSet { * @return {@code true} if the set contains this type; otherwise {@code false} */ public boolean containsType(CharSequence internalName) { - final String[] table = this.table; + final int h = hash(internalName.hashCode()); + final int slotMask = this.slotMask; - for (int i = 1, h = internalName.hashCode(); true; i++, h = rehash(h)) { - String existing = table[slotMask & h]; - if (existing != null) { - // use content-equality, not object-equality - if (existing.contentEquals(internalName)) { - return true; - } else if (i < MAX_HASH_ATTEMPTS) { - continue; // rehash and try again - } - } - return false; - } - } + final int[] hashes = this.hashes; + final String[] table = this.table; - /** - * Attempts to add a type to a hashtable of names, with a bounded amount of rehashing. - * - * @param table a hashtable of internal names - * @param slotMask mask used to map hashes to slots - * @param internalName the internal name of the type - * @return {@code true} if the type was successfully added; otherwise {@code false} - */ - private static boolean add(String[] table, int slotMask, String internalName) { - for (int i = 1, h = internalName.hashCode(); true; i++, h = rehash(h)) { - int slot = slotMask & h; - String existing = table[slot]; - // be prepared to de-duplicate names - if (existing == null || existing.equals(internalName)) { - table[slot] = internalName; + int hashed, slot = h & slotMask; + while ((hashed = hashes[slot]) != 0) { + if (hashed == h && table[slot].contentEquals(internalName)) { return true; - } else if (i < MAX_HASH_ATTEMPTS) { - continue; // rehash and try again } - return false; + slot = (slot + 1) & slotMask; } + return false; } - private static int rehash(int oldHash) { - return Integer.reverseBytes(oldHash * 0x9e3775cd) * 0x9e3775cd; + private static int hash(int h) { + return h != 0 ? h : 0xDD06; // reserve 0 for "no hash" } // ---------------------------------------------------------------------------------------------- diff --git a/class-match/src/test/java/datadog/instrument/classmatch/InternalNamesTest.java b/class-match/src/test/java/datadog/instrument/classmatch/InternalNamesTest.java new file mode 100644 index 0000000..28e1c82 --- /dev/null +++ b/class-match/src/test/java/datadog/instrument/classmatch/InternalNamesTest.java @@ -0,0 +1,214 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2025-Present Datadog, Inc. + */ + +package datadog.instrument.classmatch; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.Predicate; +import org.junit.jupiter.api.Test; + +class InternalNamesTest { + private static final List COLLISIONS = + asList("pkg.AaAa", "pkg.AaBB", "pkg.BBAa", "pkg.BBBB"); + + @Test + void empty() { + InternalNames names = new InternalNames(Collections.emptyList()); + assertEquals(0, names.size()); + assertFalse(names.containsType("")); + assertFalse(names.iterator().hasNext()); + assertThrows(NoSuchElementException.class, () -> names.iterator().next()); + } + + @Test + void normalNames() { + checkNames( + asList("java.lang.String", "java/lang/String", "pkg.Type", "pkg.Type$Nested", "例.型", "")); + } + + @Test + void largeSet() { + List types = new ArrayList<>(); + for (int i = 0; i < 512; i++) { + types.add("pkg.Type" + i); + } + checkNames(types); + } + + @Test + void collidingNamesFormProbeRun() { + // The first four names collide in a 16-slot table. + InternalNames names = + checkNames(asList("pkg.Type0", "pkg.Type17", "pkg.Type28", "pkg.Type39", "例.型", "")); + // These misses start at the same slot as the colliding names. + assertMissing(names, "pkg/Type110", "pkg/Type121"); + } + + @Test + void neighbouringProbeRunsMerge() { + // Names starting in adjacent slots form a shared probe run. + InternalNames names = + checkNames(asList("pkg.Type0", "pkg.Type1", "pkg.Type17", "pkg.Type18", "例.型", "")); + // The miss must probe past names from both starting slots. + assertMissing(names, "pkg/Type29"); + } + + @Test + void probeRunWrapsAround() { + // Collisions at slot 15 wrap around a 16-slot table. + InternalNames names = + checkNames(asList("pkg.Type13", "pkg.Type24", "pkg.Type35", "pkg.Type14", "", "例.型")); + // Misses must cross the end of the table too. + assertMissing(names, "pkg/Type46", "pkg/Type25"); + } + + @Test + void deduplicatesNormalizedNames() { + checkNames( + asList( + "pkg/AaAa", + "pkg.AaAa", + "pkg.AaBB", + "pkg.BBAa", + "pkg.BBBB", + "pkg.AaAa", + "pkg.AaBB", + "pkg.BBAa", + "pkg.BBBB", + "pkg/AaAa", + "pkg.Type", + "pkg.Type$Nested", + "例.型", + "")); + } + + @Test + void probeRunComparesPrefixesAndUnicode() { + // Short names and Unicode characters share probe runs. + InternalNames names = checkNames(asList("", "h", "hh", "hhh", "p", "\u0100")); + assertMissing(names, "hhhh", "x", "\u0100h", "\ufff8"); + } + + @Test + void manyIdenticalHashesAndMissingNames() { + List types = new ArrayList<>(); + for (int i = 0; i < 256; i += 2) { + types.add(collidingName(i)); + } + InternalNames names = checkNames(types); + for (int i = 0; i < 256; i++) { + String name = collidingName(i); + assertEquals(i % 2 == 0, names.containsType(query(name))); + } + } + + @Test + void matcherFactoriesSupportCollisions() { + TypeMatcher matcher = TypeMatcher.typeOneOf(COLLISIONS); + for (String type : COLLISIONS) { + assertTrue(matcher.test(query(InternalMatchers.internalName(type)))); + } + assertFalse(matcher.test(query("pkg/AaAaAa"))); + Predicate annotations = InternalMatchers.declaresAnnotationOneOf(COLLISIONS); + assertTrue(annotations.test(new String[] {"pkg/Unknown", "pkg/BBBB"})); + assertFalse(annotations.test(new String[] {"pkg/Unknown"})); + } + + @Test + void setContract() { + InternalNames names = new InternalNames(asList("pkg.Type", "pkg.Other")); + Iterator iterator = names.iterator(); + Set actual = new HashSet<>(); + while (iterator.hasNext()) { + assertTrue(iterator.hasNext()); + assertTrue(actual.add(iterator.next())); + } + assertEquals(new HashSet<>(asList("pkg/Type", "pkg/Other")), actual); + assertThrows(UnsupportedOperationException.class, iterator::remove); + assertThrows(NoSuchElementException.class, iterator::next); + assertFalse(names.contains(null)); + assertFalse(names.contains(123)); + } + + private static InternalNames checkNames(List types) { + InternalNames names = new InternalNames(types); + Set expected = new HashSet<>(); + for (String type : types) { + String name = InternalMatchers.internalName(type); + expected.add(name); + assertTrue(names.containsType(name), name); + assertTrue(names.contains(query(name)), name); + assertTrue(names.containsType(typeString(name)), name); + } + assertEquals(expected.size(), names.size()); + assertEquals(expected, new HashSet<>(names)); + assertMissing(names, "!", "pkg/", "pkg/Type$", "pkg/Type$NestedExtra", "例/型別", "\uffff"); + return names; + } + + private static void assertMissing(InternalNames names, String... missing) { + for (String name : missing) { + assertFalse(names.containsType(name), name); + assertFalse(names.containsType(query(name)), name); + assertFalse(names.containsType(typeString(name)), name); + } + } + + private static String collidingName(int bits) { + StringBuilder name = new StringBuilder("pkg/"); + for (int i = 0; i < 8; i++) { + name.append((bits & (1 << i)) == 0 ? "Aa" : "BB"); + } + return name.toString(); + } + + private static TypeString typeString(String name) { + String descriptor = "L" + name + ";"; + return new TypeString(descriptor, 1, descriptor.length() - 1, name.hashCode()); + } + + /** String-compatible hash, identity equality, and no conversion to String allowed. */ + private static CharSequence query(String value) { + return new CharSequence() { + @Override + public int length() { + return value.length(); + } + + @Override + public char charAt(int index) { + return value.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + throw new AssertionError("Lookup must not allocate a String"); + } + }; + } +} diff --git a/testing/src/main/java/datadog/instrument/testing/SampleClasses.java b/testing/src/main/java/datadog/instrument/testing/SampleClasses.java index 07c81d4..4dbd4c6 100644 --- a/testing/src/main/java/datadog/instrument/testing/SampleClasses.java +++ b/testing/src/main/java/datadog/instrument/testing/SampleClasses.java @@ -6,10 +6,15 @@ package datadog.instrument.testing; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; + +import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.UncheckedIOException; import java.util.List; import java.util.Objects; @@ -17,12 +22,29 @@ import java.util.stream.Collectors; /** - * Provides access to external bytecode declared using the {@code sampleBytecode} gradle dependency. + * Sample class names, plus bytecode declared using the {@code sampleBytecode} gradle dependency. */ public final class SampleClasses { /** The directory containing the sample bytecode jars. */ - public static final String SAMPLE_BYTECODE_DIRECTORY = "build/sampleBytecode/"; + private static final String SAMPLE_BYTECODE_DIRECTORY = "build/sampleBytecode/"; + + /** + * Loads shared corpus of dotted class names for tests and benchmarks. + * + * @return large list of dotted class names + */ + public static List loadClassNames() { + try (InputStream in = + requireNonNull( + SampleClasses.class.getResourceAsStream("sample_class_names.txt"), + "sample_class_names.txt"); + BufferedReader reader = new BufferedReader(new InputStreamReader(in, UTF_8))) { + return reader.lines().collect(Collectors.toList()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } /** * Loads all class bytecode from the named sample bytecode jar. @@ -52,8 +74,8 @@ public static List loadBytecode(String sampleJar) { }) .filter(Objects::nonNull) .collect(Collectors.toList()); - } catch (IOException ex) { - throw new UncheckedIOException(ex); + } catch (IOException e) { + throw new UncheckedIOException(e); } } } diff --git a/utils/src/test/resources/sample_class_names.txt b/testing/src/main/resources/datadog/instrument/testing/sample_class_names.txt similarity index 100% rename from utils/src/test/resources/sample_class_names.txt rename to testing/src/main/resources/datadog/instrument/testing/sample_class_names.txt diff --git a/utils/src/jmh/java/datadog/instrument/utils/ClassInfoCacheBenchmark.java b/utils/src/jmh/java/datadog/instrument/utils/ClassInfoCacheBenchmark.java index 358c14c..44c2ef8 100644 --- a/utils/src/jmh/java/datadog/instrument/utils/ClassInfoCacheBenchmark.java +++ b/utils/src/jmh/java/datadog/instrument/utils/ClassInfoCacheBenchmark.java @@ -3,12 +3,9 @@ import static java.util.concurrent.TimeUnit.MICROSECONDS; import static org.openjdk.jmh.annotations.Mode.AverageTime; -import java.io.IOException; -import java.io.UncheckedIOException; +import datadog.instrument.testing.SampleClasses; import java.net.URL; import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.List; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -38,11 +35,7 @@ public class ClassInfoCacheBenchmark { @Setup(Level.Trial) public void setup() { - try { - classNames = Files.readAllLines(Paths.get("src/test/resources/sample_class_names.txt")); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + classNames = SampleClasses.loadClassNames(); cl = URLClassLoader.newInstance(new URL[0]); cache = new ClassInfoCache<>(cacheSize); infoToShare = new Object(); diff --git a/utils/src/jmh/java/datadog/instrument/utils/ClassNameFilterBenchmark.java b/utils/src/jmh/java/datadog/instrument/utils/ClassNameFilterBenchmark.java index 25864b9..e63dc87 100644 --- a/utils/src/jmh/java/datadog/instrument/utils/ClassNameFilterBenchmark.java +++ b/utils/src/jmh/java/datadog/instrument/utils/ClassNameFilterBenchmark.java @@ -3,10 +3,7 @@ import static java.util.concurrent.TimeUnit.MICROSECONDS; import static org.openjdk.jmh.annotations.Mode.AverageTime; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.Paths; +import datadog.instrument.testing.SampleClasses; import java.util.List; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -34,11 +31,7 @@ public class ClassNameFilterBenchmark { @Setup(Level.Trial) public void setup() { - try { - classNames = Files.readAllLines(Paths.get("src/test/resources/sample_class_names.txt")); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + classNames = SampleClasses.loadClassNames(); cache = new ClassNameFilter(cacheSize); } diff --git a/utils/src/jmh/java/datadog/instrument/utils/ClassNameTrieBenchmark.java b/utils/src/jmh/java/datadog/instrument/utils/ClassNameTrieBenchmark.java index 22cad91..b7d1388 100644 --- a/utils/src/jmh/java/datadog/instrument/utils/ClassNameTrieBenchmark.java +++ b/utils/src/jmh/java/datadog/instrument/utils/ClassNameTrieBenchmark.java @@ -3,10 +3,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.openjdk.jmh.annotations.Mode.AverageTime; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.Paths; +import datadog.instrument.testing.SampleClasses; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -36,11 +33,7 @@ public class ClassNameTrieBenchmark { @Setup(Level.Trial) public void setup() { - try { - classNames = Files.readAllLines(Paths.get("src/test/resources/sample_class_names.txt")); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + classNames = SampleClasses.loadClassNames(); classNameTrie = buildClassNameTrie(); radixTrie = buildRadixTrie(); }