Repository navigation
Commit 7a4e0bd
perf(router): make fewer calls per part when building the tree (#195)
<!-- claude-projects-attribution: {"github_login":"dinwwwh"} -->
_Requested by **dinwwwh** · [project
thread](https://claude.ai/code/project/chan_01UcKMz91tMarPV1kWLVgLYE?thread=cmsg_01UcKMz91tMarPV1kWLVgLYE6swgSUM9SyULmBVdRjY2t7)_
## Summary
A cold start builds the tree in V8's interpreter and baseline code,
where each call and each property load goes through a builtin. In the
profile of the first request for a method (PATCH in the REST set, 100
routes), loads and calls are most of the time outside `split`. Two
changes in `expand`'s part loop:
- `decode(part)` became `part.includes('%') ? decode(part) : part`, so a
part without `%` (nearly all of them) skips the call.
- `literals = Math.min(literals, index)` became `if (index < literals)
literals = index`, which drops a global load, a property load and a call
per waiting route and per param.
Stryker showed a gap the second change exposed: with the comparison
mutated to `true`, `literals` moves to the last param, and a late-added
route sharing a param part with the route before it skipped that param.
A case in `late-adds.test.ts` now covers it (`/s/{x}/t/{y}` then
`/s/{x}/t/u`, added after a request).
## Measurements
Callgrind instructions, `--single-threaded`, hash seeds 1 and 2
(averaged), main vs this PR:
| Stage | main | PR | Change |
| --- | --- | --- | --- |
| REST, 3 requests | 3.892M | 3.757M | -3.5% |
| REST, 3 requests on 3 methods | 5.697M | 5.487M | -3.7% |
| REST, 10 requests | 6.615M | 6.452M | -2.5% |
| 100 real nouns, 10 requests | 6.588M | 6.426M | -2.5% |
| RPC (statics only), 10 requests | 420k | 420k | same |
| Router import | | | +6k (+0.4% of the router's share) |
- Size (esbuild `--minify`, gzip -9): 5068 B to 5076 B minified, 2112 B
to 2114 B gzip.
- Steady state: unchanged, since `expand` only runs while building the
tree.
## Checks
- `pnpm run check` green; router tests 73/73 with 100% coverage.
- All fuzzers at 3 seeds: 0 diffs.
- Stryker: 54 survivors (61 on main). The new one, `part.includes("")`,
only makes every part go through `decode`, which checks for `%` itself.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01VVaw7Sutbw6xMTMyDFeNfw)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent b2a5f7d commit 7a4e0bd
2 files changed
Lines changed: 12 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
384 | 384 | | |
385 | 385 | | |
386 | 386 | | |
387 | | - | |
| 387 | + | |
388 | 388 | | |
389 | 389 | | |
390 | 390 | | |
391 | 391 | | |
392 | 392 | | |
393 | 393 | | |
394 | 394 | | |
395 | | - | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
396 | 398 | | |
397 | 399 | | |
398 | 400 | | |
| |||
408 | 410 | | |
409 | 411 | | |
410 | 412 | | |
411 | | - | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
412 | 416 | | |
413 | 417 | | |
414 | 418 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
18 | 21 | | |
19 | 22 | | |
20 | 23 | | |
| |||
26 | 29 | | |
27 | 30 | | |
28 | 31 | | |
| 32 | + | |
| 33 | + | |
29 | 34 | | |
30 | 35 | | |
31 | 36 | | |
| |||
0 commit comments