Raise CACHE_LIMIT to 4096 - #186
Open
jdalton wants to merge 1 commit into
Open
Conversation
Sweeping the limit over three workloads, with retained heap alongside throughput, because the question is a trade rather than a maximum:
workload 1000 2048 4096 8192 30 selectors, all hit 26.18us 26.93us 26.80us 27.27us 2000 selectors 50.75ms 50.36ms 4.12ms 4.15ms heap, cache full 2.99mb 3.86mb 6.87mb 14.05mb
A working set that fits costs the same at any limit. One that fits 4096 and not 1000 is worth 12x. 8192 buys nothing further and doubles the worst case, which is only reached by a caller that has that many distinct selectors, since the caches grow lazily.
The cliff sits between 2048 and 4096 for a set of 2000 selectors because a selector is not one cache entry: ':not(.x)' compiles to a run-time s.match('.x', e), so the argument takes an entry of its own. 100 such selectors leave 200 entries in the match caches, measured.
4096 is also the value the fork of this engine inside jsdom's current selector implementation uses.
References:
- MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
- Reading: https://zod.dev/blog/reducing-memory-footprint — why a cache size is measured rather than chosen
jdalton
force-pushed
the
perf/cache-limit
branch
from
September 4, 2026 18:00
3703b74 to
a412975
Compare
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.
A working set that fits costs the same at any limit, one that fits 4096 and not 1000 is worth 12x, and 8192 buys nothing further while doubling the worst-case heap. 4096 is also what the fork of this engine inside jsdom uses.
Detail, and how it was checked
Sweeping the limit over three workloads, with retained heap alongside throughput, because the question is a trade rather than a maximum:
workload 1000 2048 4096 8192 30 selectors, all hit 26.18us 26.93us 26.80us 27.27us 2000 selectors 50.75ms 50.36ms 4.12ms 4.15ms heap, cache full 2.99mb 3.86mb 6.87mb 14.05mb
A working set that fits costs the same at any limit. One that fits 4096 and not 1000 is worth 12x. 8192 buys nothing further and doubles the worst case, which is only reached by a caller that has that many distinct selectors, since the caches grow lazily.
The cliff sits between 2048 and 4096 for a set of 2000 selectors because a selector is not one cache entry: ':not(.x)' compiles to a run-time s.match('.x', e), so the argument takes an entry of its own. 100 such selectors leave 200 entries in the match caches, measured.
4096 is also the value the fork of this engine inside jsdom's current selector implementation uses.
Extracted from #167 as a standalone change: one file, applies to master on its own, and checked against the benchmark fixture to confirm every selector still agrees with the native engine.
References: the spec, the browser source, and what each part was reasoned from
This patch applies to master on its own. The sixteen in this series were checked by cherry-picking them onto master one after another, in this order and in reverse, and all sixteen land without a conflict.