Repository navigation
refactor(router): build a method's tree in match, without a helper - #198
Conversation
`build` was a two-line function called only from `match`. Each function costs V8 about 15k instructions to set up at import and again when it is first compiled, so folding it in makes the module smaller and the first request that builds a tree cheaper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7Sutbw6xMTMyDFeNfw
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merging this PR will degrade performance by 22.34%2 warnings
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Inline
buildintomatch— the module-private two-linebuild(routes)helper (its sole caller wasmatch) is folded into theroutes.pending.length > 0branch, with its doc comment carried over. The inlinedexpand(routes.root, routes.pending, 1, routes.unfiltered > 0)+routes.pending = []are statement-for-statement identical to the removed body, so matching behavior is unchanged.
Verified: build( has no remaining references, expand is resolved in the same module, and the router suite passes (6 files / 73 tests).
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
|
CodSpeed's −22% on "match, 250 RPC-style procedures" comes from when V8 compiles CodSpeed runs with
From the 9th call on, both versions take 27.9k instructions per call. With TurboFan on, the steady state is also unchanged: rpc 1589 vs 1589 and static 1644 vs 1645 instructions per request. This is the same Sparkplug shift as on #167 and #161. I'm merging on that basis. Generated by Claude Code |
<!-- claude-projects-attribution: {"github_login":"dinwwwh"} -->
_Requested by **dinwwwh** · [project
thread](https://claude.ai/code/project/chan_01UcKMz91tMarPV1kWLVgLYE?thread=cmsg_01UcKMz91tMarPV1kWLVgLYE6swgSUM9SyULmBVdRjY2t7)_
## What
`matchesSegment` was a one-line function used in two places:
`matchesCatchAll`, for the segments after a catch-all param, and the
closure that `createPrefixMatcher` returns. Both now do the comparison
in place, with a comment that `undefined` stands for a param, which
needs a non-empty segment.
## Why
Each top-level function costs V8 about 15k instructions to set up at
import, and about as much again to compile when it is first called (see
#198). This removes one function from every import. It also removes one
compile from the first request that reaches a catch-all, or a prefix
with a param.
## Measurements
Instruction counts from callgrind, `node --single-threaded`, averaged
over hash seeds 1 and 2:
| | before | after |
|---|---|---|
| min / min+gzip | 5043 / 2100 B | 5053 / 2098 B |
| import | 2.167M | 2.164M |
| REST cold+10 (includes a catch-all request) | 6.041M | 6.017M (−0.4%)
|
| steady, catch-all / tail | 4985 / 5256 | 4979 / 5249 |
| steady, prefix matcher with a param / with `%` | 2453 / 4082 | 2422 /
4052 (−1%) |
Cold+3 on the REST set reaches no catch-all, so it changes only by the
import saving.
## Checks
- Tests pass with 100% coverage.
- The model fuzzers, including the late-add and prefix ones, find 0
diffs.
- Stryker finds 68 survivors, the same as on main. None are on the new
comparisons.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01VVaw7Sutbw6xMTMyDFeNfw)_
Co-authored-by: Claude <noreply@anthropic.com>

Requested by dinwwwh · project thread
What
buildwas a two-line function that onlymatchcalled. Its two lines now sit inmatch, and its doc comment moves along with them.Why
V8 spends about 15k instructions on each top-level function: once at import, and again the first time it compiles the function. I measured this with callgrind on 40 tiny functions, each called once, against one function called 40 times. Folding
buildin drops one function from every import, and one compile from the first request that builds a tree. The cost is about 70 bytes more formatchto compile on any app's first request.Measurements
Instruction counts from callgrind,
node --single-threaded, averaged over hash seeds 1 and 2. The route sets are oRPC-style: 250 static RPC routes and 703 REST routes.matchcompiles slightly more code.Checks
Generated by Claude Code