perf(router-core): memoize searchStr by search-object identity in buildLocation - #8143
perf(router-core): memoize searchStr by search-object identity in buildLocation#8143Sheraff wants to merge 2 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit 0e2312b
☁️ Nx Cloud last updated this comment at |
Bundle Size Benchmarks
The following scenarios have bundle-size changes compared with the baseline:
Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better. |
🚀 Changeset Version Preview4 package(s) bumped directly, 19 bumped as dependents. 🟩 Patch bumps
|
Merging this PR will degrade performance by 8.34%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | mem server error-paths redirect (solid) |
382.2 KB | 855.9 KB | -55.34% |
| ❌ | Memory | mem client unique-location-churn (solid) |
265.7 KB | 306 KB | -13.17% |
| ❌ | Memory | mem server error-paths unmatched (vue) |
577.6 KB | 641.3 KB | -9.92% |
| ❌ | Memory | mem client unique-location-churn (vue) |
467.7 KB | 513 KB | -8.83% |
| ❌ | Memory | mem server peak-large-page (solid) |
1 MB | 1.1 MB | -5.64% |
| ❌ | Memory | mem server server-fn-churn (react) |
378.9 KB | 393.5 KB | -3.73% |
| ⚡ | Memory | mem server error-paths unmatched (react) |
544.1 KB | 435.3 KB | +24.99% |
| ⚡ | Memory | mem server error-paths redirect (react) |
318.9 KB | 296.4 KB | +7.59% |
| ⚡ | Memory | mem server aborted-requests (vue) |
1.1 MB | 1 MB | +4.19% |
| ⚡ | Memory | mem server peak-large-page (react) |
1.2 MB | 1.2 MB | +3.32% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing perf/task2-search-reserialization (0e2312b) with main (d83a896)
There was a problem hiding this comment.
Nx Cloud is proposing a fix for your failed CI:
We updated the hash updater callbacks in the two new test files added by this PR to use optional parameters (prev?: string) instead of required ones, which fixes the TS2322 type errors caught by TypeScript TS56. The Updater<string> type expects (prev?: string | undefined) => string, and the ?? '' fallback preserves existing test assertions (e.g. '/?page=1#x-0' when no prior hash exists).
Tip
✅ We verified this fix by re-running @tanstack/router-core:test:types, @tanstack/router-core:test:unit.
Suggested Fix changes
diff --git a/packages/router-core/tests/buildLocationSearchStr.bench.ts b/packages/router-core/tests/buildLocationSearchStr.bench.ts
index 43c92539..7e8c48a5 100644
--- a/packages/router-core/tests/buildLocationSearchStr.bench.ts
+++ b/packages/router-core/tests/buildLocationSearchStr.bench.ts
@@ -32,7 +32,7 @@ expect(loc.searchStr).toBe('?page=1')
const hashLoc = warm.buildLocation({
to: '/',
search: true,
- hash: (prev: string) => prev,
+ hash: (prev?: string) => prev ?? '',
})
expect(hashLoc.href.startsWith('/?page=1')).toBe(true)
@@ -43,8 +43,7 @@ describe('router.buildLocation - repeated same-search navigations', () => {
const router = makeRouter()
let size = 0
for (let i = 0; i < iterations; i++) {
- size += router.buildLocation({ to: '/', search: true }).searchStr
- .length
+ size += router.buildLocation({ to: '/', search: true }).searchStr.length
}
benchmarkSink = size
},
diff --git a/packages/router-core/tests/searchStrMemo.test.ts b/packages/router-core/tests/searchStrMemo.test.ts
index 3c2c5dfe..4e5c651f 100644
--- a/packages/router-core/tests/searchStrMemo.test.ts
+++ b/packages/router-core/tests/searchStrMemo.test.ts
@@ -41,7 +41,7 @@ test('hash-only nav: href contains unchanged search plus hash', () => {
const h2 = router.buildLocation({
to: '/',
search: true,
- hash: (prev: string) => `x-${prev.length}`,
+ hash: (prev?: string) => `x-${(prev ?? '').length}`,
})
expect(h2.href).toBe('/?page=1#x-0')
})
Or Apply changes locally with:
npx nx-cloud apply-locally jI4t-echy
Apply fix locally with your editor ↗ View interactive diff ↗
🎓 Learn more about Self-Healing CI on nx.dev
Summary
buildLocationrannullReplaceEqualDeep(fromSearch, nextSearch)(which returns the previous reference when unchanged) but then unconditionally calledstringifySearch— rebuilding a URLSearchParams and probing every value even when nothing changed.WeakMap<object,string>, ~13 lines, router.ts only): structural sharing makes the returned reference a free memo key, so unchanged-search navigations reuse the cached string.Performance
buildLocation, same-search navsRoot cause confirmed:
stringifySearchcosts ~2×nullReplaceEqualDeepand ran unconditionally.Verification
searchStralways matches emitted search; hash-only hrefs verified (/?page=1#section) + round-trip assertions in newtests/searchStrMemo.test.ts.RESULT-perf-task2.mdon the branch.