From 428e72ff8fc8dc464de2785eae868d577b64f6ed Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Thu, 9 Jul 2026 07:55:00 -0400 Subject: [PATCH] Add bloom-filter fast-path to dense store (skip knownIndexOf scan) A per-map superset presence filter (one long): a clear bit means a known tag is definitely absent, so an append skips the O(n) linear scan -> O(1) in the common per-build case. Set on every add, never cleared on remove (stale bit only costs a scan, never a wrong answer), so correctness is independent of the position->bit collision rate; the scan is authoritative. Crude fieldPos-mod-64 mapping to start; per-type graph-coloring later raises the hit rate. WIP: tests + benchmark pending. Co-Authored-By: Claude Opus 4.8 --- .../main/java/datadog/trace/api/TagMap.java | 60 +++++++++++++++---- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/api/TagMap.java b/internal-api/src/main/java/datadog/trace/api/TagMap.java index cd5cfbeca18..1d4327f7d66 100644 --- a/internal-api/src/main/java/datadog/trace/api/TagMap.java +++ b/internal-api/src/main/java/datadog/trace/api/TagMap.java @@ -1225,11 +1225,11 @@ static final class EmptyHolder { /** * Dense known-tag store (dense-tagmap-design §5). Values for KNOWN tags (those {@link - * KnownTagCodec#keyOf} resolves to a stored id) live in these INSERTION-ORDERED parallel arrays with - * NO per-tag {@link Entry} object — the allocation win. Lazily allocated on the first known-tag - * write ({@code null} until then, so all-unknown maps pay nothing) and grown x2 from {@link - * #KNOWN_INIT_CAP}. Matched by globalSerial via a linear scan ({@link #knownIndexOf}); reads - * aren't hot, so O(knownCount) is fine and positional indexing is deferred. Dormant until a + * KnownTagCodec#keyOf} resolves to a stored id) live in these INSERTION-ORDERED parallel arrays + * with NO per-tag {@link Entry} object — the allocation win. Lazily allocated on the first + * known-tag write ({@code null} until then, so all-unknown maps pay nothing) and grown x2 from + * {@link #KNOWN_INIT_CAP}. Matched by globalSerial via a linear scan ({@link #knownIndexOf}); + * reads aren't hot, so O(knownCount) is fine and positional indexing is deferred. Dormant until a * resolver is registered: {@code keyOf} returns 0, so nothing routes here and production is * byte-identical. * @@ -1247,6 +1247,15 @@ static final class EmptyHolder { private Object[] knownValues; private int knownCount; + /** + * Bloom-style presence filter over the dense store: a set bit means {@code tagId} MAY be present + * (scan to confirm), a clear bit means it is DEFINITELY absent (skip the scan, append in O(1) — + * the common per-build case). A superset of the present ids' bits: set on every add, never + * cleared on remove (a stale bit only costs a scan, never a wrong answer). So correctness never + * depends on the position→bit collision rate; only the fast-path hit rate does. + */ + private long knownBloom; + private static final int KNOWN_INIT_CAP = 8; /** @@ -1552,27 +1561,45 @@ private void ensureKnownCapacity() { } } + /** + * Presence-filter bit for {@code tagId}. Crude position→bit map ({@code fieldPos} mod 64) to + * start; a collision-minimizing per-type coloring later only raises the hit rate — correctness + * never depends on it, because the scan is authoritative. + */ + private static long knownBloomBit(long tagId) { + return 1L << (KnownTagCodec.fieldPos(tagId) & 63); + } + /** * Stores a known tag's value densely (no {@link Entry} alloc). Overwrites in place when present * (returning the prior value materialized as an Entry, per the {@code Map} contract — usually - * discarded by {@code set}); otherwise appends, growing x2 as needed. + * discarded by {@code set}); otherwise appends, growing x2 as needed. The bloom filter skips the + * {@link #knownIndexOf} scan when the tag is definitely absent (the common per-build case), so an + * append is O(1) instead of O(n). */ private Entry putKnownValue(long tagId, Object value) { - int i = this.knownIndexOf(tagId); - if (i >= 0) { - Object prior = this.knownValues[i]; - this.knownValues[i] = value; - return materializeKnown(tagId, prior); + long bit = knownBloomBit(tagId); + if ((this.knownBloom & bit) != 0) { + // maybe present -> scan to overwrite in place + int i = this.knownIndexOf(tagId); + if (i >= 0) { + Object prior = this.knownValues[i]; + this.knownValues[i] = value; + return materializeKnown(tagId, prior); + } + // bloom false positive (collision) -> fall through to append } this.ensureKnownCapacity(); int slot = this.knownCount++; this.knownIds[slot] = tagId; this.knownValues[slot] = value; + this.knownBloom |= bit; return null; } /** Raw dense value for {@code tagId}, or {@code null} when absent (no Entry, no boxing). */ private Object knownRawValue(long tagId) { + if ((this.knownBloom & knownBloomBit(tagId)) == 0) return null; // definitely absent, no scan int i = this.knownIndexOf(tagId); return i < 0 ? null : this.knownValues[i]; } @@ -1581,6 +1608,7 @@ private Object knownRawValue(long tagId) { * Removes a known tag from the dense store (swap-with-last), returning the prior Entry or null. */ private Entry removeKnown(long tagId) { + if ((this.knownBloom & knownBloomBit(tagId)) == 0) return null; // definitely absent int i = this.knownIndexOf(tagId); if (i < 0) return null; Object prior = this.knownValues[i]; @@ -1589,6 +1617,8 @@ private Entry removeKnown(long tagId) { this.knownValues[i] = this.knownValues[last]; this.knownIds[last] = 0L; this.knownValues[last] = null; + // knownBloom intentionally NOT cleared: a stale-set bit only costs a scan; clearing could drop + // a bit still shared (via collision) by a present id -> false negative. return materializeKnown(tagId, prior); } @@ -1603,7 +1633,8 @@ private static Entry materializeKnown(long tagId, Object value) { * local bucket entry — known tags never bucket — so no bucket check is needed here.) */ private boolean parentDenseHidden(long tagId) { - if (this.knownIndexOf(tagId) >= 0) return true; // shadowed by a local dense entry + // shadowed by a local dense entry (bloom prunes the scan when definitely absent) + if ((this.knownBloom & knownBloomBit(tagId)) != 0 && this.knownIndexOf(tagId) >= 0) return true; return this.removedFromParent != null && this.removedFromParent.contains(KnownTagCodec.nameOf(tagId)); // tombstoned } @@ -2006,6 +2037,7 @@ private void putAllIntoEmptyMap(OptimizedTagMap that) { this.knownIds = Arrays.copyOf(that.knownIds, that.knownIds.length); this.knownValues = Arrays.copyOf(that.knownValues, that.knownValues.length); this.knownCount = that.knownCount; + this.knownBloom = that.knownBloom; } } @@ -2048,7 +2080,8 @@ public void fillStringMap(Map stringMap) { } for (int i = 0; i < this.knownCount; ++i) { stringMap.put( - KnownTagCodec.nameOf(this.knownIds[i]), TagValueConversions.toString(this.knownValues[i])); + KnownTagCodec.nameOf(this.knownIds[i]), + TagValueConversions.toString(this.knownValues[i])); } } @@ -2393,6 +2426,7 @@ public void clear() { this.knownIds = null; this.knownValues = null; this.knownCount = 0; + this.knownBloom = 0L; } public OptimizedTagMap freeze() {