Skip to content

Commit 5a14e62

Browse files
dougqhclaude
andcommitted
Add Java 17 results to set benchmark Javadocs
ImmutableSetBenchmark: HashSet fastest; Set.copyOf (SetN) ~10% behind on hit, the compact form the agent uses for fixed config sets. SingleThreadedSetBenchmark: uncontended synchronizedSet tax ~37% on contains (biased locking off, Java 17), near-zero on iterate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 24a9fdc commit 5a14e62

2 files changed

Lines changed: 50 additions & 3 deletions

File tree

internal-api/src/jmh/java/datadog/trace/util/ImmutableSetBenchmark.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,29 @@
3737
* </ul>
3838
*
3939
* <p>Lookups are interned (the {@code ==} fast path where a structure has one); misses are short
40-
* and never present. (Results pending a fresh multi-JVM run — {@code Set.copyOf} only materializes
41-
* the compact form on Java 10+.)
40+
* and never present.
41+
*
42+
* <p>Java 17 results (Apple M1, {@code @Fork(2)}, {@code @Threads(8)}; M ops/s = millions):
43+
*
44+
* <pre>{@code
45+
* Structure hit miss
46+
* hashSet 2159 1751 (fastest)
47+
* copyOf (SetN) 1946 1633
48+
* array 926 584
49+
* sortedArray 664 588
50+
* treeSet 642 593
51+
* }</pre>
52+
*
53+
* <p>Key findings:
54+
*
55+
* <ul>
56+
* <li>{@code HashSet} is fastest; {@link java.util.Set#copyOf} ({@code SetN}) trails by only ~10%
57+
* on hit and ~7% on miss — and it's the compact, array-backed form the agent already uses for
58+
* fixed config sets, so it's a strong default when the set is immutable.
59+
* <li>{@code array} / {@code sortedArray} / {@code treeSet} cluster at ~0.6–0.9B — they scan,
60+
* binary-search, or tree-walk per lookup, so they trail the hashed structures, most visibly
61+
* on the miss path.
62+
* </ul>
4263
*/
4364
@Fork(2)
4465
@Warmup(iterations = 2)

internal-api/src/jmh/java/datadog/trace/util/SingleThreadedSetBenchmark.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,33 @@
2929
* monitor is only ever locked by one thread: biased locking ≈ free on Java ≤ 11, full uncontended
3030
* CAS on Java 15+ (biased locking disabled by default, JEP 374). The unsynchronized {@code hashSet}
3131
* {@code contains}/{@code iterate} methods are the in-harness baseline; the tax is the delta.
32-
* (Results pending a fresh multi-JVM run.)
32+
*
33+
* <p>Java 17 results (Apple M1, {@code @Fork(2)}, {@code @Threads(8)}; M ops/s = millions):
34+
*
35+
* <pre>{@code
36+
* contains_hashSet 1291
37+
* contains_synchronizedSet 808 (~37% slower — the uncontended sync tax)
38+
* iterate_hashSet 91
39+
* iterate_synchronizedSet 90 (one monitor acquire amortized over the walk)
40+
*
41+
* create_hashSet 81 clone_hashSet 48
42+
* create_hashSet_sized 78 clone_synchronizedSet 47
43+
* create_linkedHashSet 61 clone_linkedHashSet 59
44+
* create_synchronizedSet 41 clone_treeSet 83
45+
* create_treeSet 36
46+
* }</pre>
47+
*
48+
* <p>Key findings:
49+
*
50+
* <ul>
51+
* <li><b>Uncontended synchronization tax</b> on {@code contains} is ~37% (1291 → 808M ops/s) even
52+
* with no contention and biased locking disabled (Java 17, JEP 374) — the full per-lock CAS
53+
* cost. On {@code iterate} it nearly vanishes: a single monitor acquire amortized over the
54+
* traversal.
55+
* <li>Construction: {@code TreeSet} is the slowest to build (~36M); the {@code synchronizedSet}
56+
* wrapper adds a modest cost over plain {@code HashSet}. (Allocation-path numbers carry more
57+
* run-to-run variance than the read paths.)
58+
* </ul>
3359
*/
3460
@Fork(2)
3561
@Warmup(iterations = 2)

0 commit comments

Comments
 (0)