execution/cache: give growLRU the same atomic entry count - #23523
Merged
AskAlexSharov merged 1 commit intoAug 24, 2026
Merged
Conversation
growLRU.Add ran its grow check through freelru's ShardedLRU.Len, which RLocks every shard in turn, on every add while the LRU was below its ceiling. maybeGrow scanned again inside resizeMu. CodeCache drives three of these layers (hashToCode, codeHashToCode, codeSizeByCodeHash) from the EVM code path. Reuse GenericCache's lruGen, generalised over the value type, so the count comes from an atomic maintained by add and the OnEvict callback. Len() becomes O(1) too. putContentLocked already removes an existing entry before re-adding, so every Add reaching freelru is for an absent key. BenchmarkGrowLRUParallelAddGrow, M4 Max, -cpu 10: 756.9n -> 96.6n (-87.2%, p=0.002 n=6). CodeCache.Put end-to-end does not move: addrToHash is a hashicorp lru.Cache behind one global mutex, which dominates that path. Claude-Session: https://claude.ai/code/session_01WFkAYPPhqPe1NXg41Nph78
awskii
approved these changes
Aug 24, 2026
AskAlexSharov
merged commit Aug 24, 2026
d6690ac
into
alex/cache_len_contention_37
248 checks passed
AskAlexSharov
added a commit
that referenced
this pull request
Aug 24, 2026
Stacked on #23522 — review that first; this PR's own diff is the second commit. `growLRU.Add` ran its grow check through `freelru.ShardedLRU.Len`, which RLocks every shard in turn, on **every add** while the LRU was below its ceiling; `maybeGrow` scanned again inside `resizeMu`. `CodeCache` drives three of these layers (`hashToCode`, `codeHashToCode`, `codeSizeByCodeHash`) straight off the EVM code path. Same fix as #23522: reuse `lruGen`, generalised over the value type, so the count comes from an atomic maintained by `add` and the `OnEvict` callback. `Len()` becomes O(1) too. `putContentLocked` already removes an existing entry before re-adding, so every `Add` reaching freelru is for an absent key — the invariant the counter needs. `BenchmarkGrowLRUParallelAddGrow` (added), M4 Max, `-cpu 10`: ``` │ growLRU_Len │ growLRU_counter │ │ sec/op │ sec/op vs base │ GrowLRUParallelAddGrow 756.90n ± 8% 96.58n ± 13% -87.24% (p=0.002 n=6) ``` `CodeCache.Put` end-to-end does **not** move (500.2n → 497.1n, p=0.394): `addrToHash` / `addrToCodeHash` are `hashicorp/golang-lru` caches behind a single global mutex, and that dominates the put path. This PR removes real CPU time from the content layers; unlocking it end-to-end needs that addr LRU sharded, which is separate work. Two tests pin counter == the LRU's real length: `growLRU` directly across add / remove / eviction / grow / `Purge`, and all three `CodeCache` layers under concurrent puts. Both verified to fail against a mutated counter. No TDD cycle: performance refactor with no intended behaviour change. https://claude.ai/code/session_01WFkAYPPhqPe1NXg41Nph78
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #23522 — review that first; this PR's own diff is the second commit.
growLRU.Addran its grow check throughfreelru.ShardedLRU.Len, which RLocks every shard in turn, on every add while the LRU was below its ceiling;maybeGrowscanned again insideresizeMu.CodeCachedrives three of these layers (hashToCode,codeHashToCode,codeSizeByCodeHash) straight off the EVM code path.Same fix as #23522: reuse
lruGen, generalised over the value type, so the count comes from an atomic maintained byaddand theOnEvictcallback.Len()becomes O(1) too.putContentLockedalready removes an existing entry before re-adding, so everyAddreaching freelru is for an absent key — the invariant the counter needs.BenchmarkGrowLRUParallelAddGrow(added), M4 Max,-cpu 10:CodeCache.Putend-to-end does not move (500.2n → 497.1n, p=0.394):addrToHash/addrToCodeHasharehashicorp/golang-lrucaches behind a single global mutex, and that dominates the put path. This PR removes real CPU time from the content layers; unlocking it end-to-end needs that addr LRU sharded, which is separate work.Two tests pin counter == the LRU's real length:
growLRUdirectly across add / remove / eviction / grow /Purge, and all threeCodeCachelayers under concurrent puts. Both verified to fail against a mutated counter.No TDD cycle: performance refactor with no intended behaviour change.
https://claude.ai/code/session_01WFkAYPPhqPe1NXg41Nph78