Repository navigation
test(router): repeat the requests of each match bench so a compile can't land in CodSpeed's measured call - #345
Conversation
…ead of dropping the filter Adding a static route after the tree has served FILTER_AFTER requests used to drop the filter, so the next request rebuilt it over every static path. Adding routes between requests, as lazy routers do, then cost a pass over all statics per add. Marking the new path's slot keeps the filter a superset of the statics at O(1) per add. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7PzysACDLzaReKDUKE
…t char The filter slot was the length modulo 8 next to the last char, so paths 8 apart in length shared a slot whenever they ended in the same char. On the GitHub, Stripe and Kubernetes path sets, 20-38% of param requests passed the filter into a wasted statics lookup; with this slot 8-22% do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7PzysACDLzaReKDUKE
…n't land in CodSpeed's measured call Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7PzysACDLzaReKDUKE
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Repeat the match request lists 16x per call —
packages/router/benches/router.bench.tsaddsREPEAT = 16and wraps the request loop of both match benches (ours and rou3) in it, so a one-time V8 compile finishes during warmup instead of landing in CodSpeed's single measured call. Theaddand prefix benches are untouched, andfresh()is still called per request inside the loop, so per-request cost stays representative for both routers. - Bench-only — no production source is touched; the branch-vs-base diff is this one file. The repeat workaround is the right lever here, since CodSpeed simulation mode selects its own warmup/measure iteration counts and vitest
benchwarmup options would not take effect.
Verified locally: uncheck packages/router/benches/router.bench.ts (oxlint, oxfmt, tsc) passes.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
Merging this PR will degrade performance by 77.32%1 warning
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
|
The CodSpeed result is expected. Each call of the 4 match benches now makes its request list 16 times, so the base and head numbers measure different amounts of work. Head is still well under 16x the base: 3.6x (RPC, ours), 7.4x (REST, ours), 4.1x and 3.5x (rou3). That gap is the point of this PR. Each single-pass base number included one-time V8 compile work that landed in the measured call. For example, the old RPC number of 47.2 µs for 4 requests is about 12 µs per request. With 16 passes, that one-time work is spread across many more requests, so it no longer dominates the result. These results become the new baseline. No router code changed. Generated by Claude Code |

Requested by dinwwwh · project thread
What
The two match benches (250 RPC-style and 703 REST-style procedures) now run their request list 16 times per call, for both this router and rou3. The add bench and the prefix bench are unchanged. This is a bench-only change with no source changes.
Why
In simulation mode, CodSpeed runs each bench function a few times as warmup and then measures one call. The RPC match bench makes only 4 requests per call, about 30k instructions. A one-time V8 event, such as a Sparkplug batch compile, can land in that call and swing the result by up to 10x. This caused the false alarms on #233 (rou3 prefix -12.9%) and #336 (RPC match x2.1).
I replayed the bench the way the plugin runs it. The replay used
optimizeFunction-style warmup calls, thengc(), then one call betweenInstrumentHooks.start/stopBenchmark, under callgrind with CodSpeed's analysis V8 flags (--no-opt --predictable ...). Instructions in the measured call:With one pass, the result depends on where the compile lands: in one case 7 warmups read 10x higher than 9. With 16 passes, it is the same for every warmup count.
The stable numbers also show something the noise hid. Since #336, this request mix costs about 23% more in the tiers CodSpeed runs, because the
POST /resource49/unknownmiss now splits the path and walks the tree. In TurboFan that is about +450 instructions per miss on a method with only static routes, which #336 accepted. So expect CodSpeed on this PR to show every match bench about 16x slower, because each call does 16x the work. Later PRs then compare against a stable baseline.I tried the same check on the prefix bench, and it is already stable with one pass (142,366 instructions for 5, 7 and 9 warmups). It stays as it is.
Checks
vitest bench --run packages/routerruns, and the hooks (oxlint, oxfmt, tsc) pass.Generated by Claude Code