From 339a0244cdd9260ff52a13155461e1e0c65d565f Mon Sep 17 00:00:00 2001 From: bm1549 Date: Thu, 9 Apr 2026 12:10:05 -0400 Subject: [PATCH] perf: unroll first probe in ClassNameFilter.contains() Unroll the first hash probe out of the loop since it is the most common path (hit on first try). Also inline the rehash function in the collision loop to avoid method call overhead. Benchmark results (ClassNameFilterBenchmark, spring-web.jar dataset): Single-threaded: cacheSize=4096: 6.458 -> 6.166 us/op (-4.5%) cacheSize=65536: 6.654 -> 6.304 us/op (-5.3%) Multi-threaded (10 threads): cacheSize=4096: 12.006 -> 8.726 us/op (-27.3%) cacheSize=16384: 11.112 -> 9.327 us/op (-16.1%) cacheSize=65536: 10.865 -> 8.604 us/op (-20.8%) cacheSize=1048576:11.140 -> 8.695 us/op (-22.0%) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../instrument/utils/ClassNameFilter.java | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/utils/src/main/java/datadog/instrument/utils/ClassNameFilter.java b/utils/src/main/java/datadog/instrument/utils/ClassNameFilter.java index c301309..a57049d 100644 --- a/utils/src/main/java/datadog/instrument/utils/ClassNameFilter.java +++ b/utils/src/main/java/datadog/instrument/utils/ClassNameFilter.java @@ -64,19 +64,28 @@ public boolean contains(CharSequence className) { final long[] members = this.members; final int slotMask = this.slotMask; - // try to find matching slot, rehashing after each attempt - for (int i = 1, h = hash; true; i++, h = rehash(h)) { - long codeAndHash = members[slotMask & h]; - if (codeAndHash != 0) { - // check hash first as it's cheap, then check class-code - if ((int) codeAndHash == hash) { - return checkClassCode(className, (int) (codeAndHash >>> 32)); - } else if (i < MAX_HASH_ATTEMPTS) { - continue; // rehash and try again - } - } + // first probe without rehash (most common path) + long codeAndHash = members[slotMask & hash]; + if (codeAndHash == 0) { return false; } + if ((int) codeAndHash == hash) { + return checkClassCode(className, (int) (codeAndHash >>> 32)); + } + + // subsequent probes with rehash (collision path) + int h = hash; + for (int i = 2; i <= MAX_HASH_ATTEMPTS; i++) { + h = Integer.reverseBytes(h * 0x9e3775cd) * 0x9e3775cd; + codeAndHash = members[slotMask & h]; + if (codeAndHash == 0) { + return false; + } + if ((int) codeAndHash == hash) { + return checkClassCode(className, (int) (codeAndHash >>> 32)); + } + } + return false; } /**