enhance(normalizr,endpoint): Reduce allocations in hot cache paths#3879
enhance(normalizr,endpoint): Reduce allocations in hot cache paths#3879
Conversation
Inline getCacheKey in GlobalCache to avoid eagerly creating both localCache and cycleCache Maps per entity type. Replace push(...spread) with an indexed for-loop when copying cached entity dependencies. Pre-create _removeSchema in Collection.CreateMover so normalizeMove no longer calls Object.create on every invocation, eliminating hidden class polymorphism that caused V8 "wrong call target" deoptimizations. Made-with: Cursor
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Size Change: +29 B (+0.04%) Total Size: 80.6 kB 📦 View Changed
ℹ️ View Unchanged
|
There was a problem hiding this comment.
Benchmark React
Details
| Benchmark suite | Current: 2a50525 | Previous: 98a7831 | Ratio |
|---|---|---|---|
data-client: getlist-100 |
177.01 ops/s (± 4.6%) |
177.01 ops/s (± 5.0%) |
1 |
data-client: getlist-500 |
46.51 ops/s (± 5.7%) |
48.47 ops/s (± 5.3%) |
1.04 |
data-client: update-entity |
476.19 ops/s (± 4.6%) |
416.67 ops/s (± 6.3%) |
0.88 |
data-client: update-user |
454.55 ops/s (± 5.6%) |
434.78 ops/s (± 5.7%) |
0.96 |
data-client: getlist-500-sorted |
51.28 ops/s (± 5.1%) |
50.13 ops/s (± 7.4%) |
0.98 |
data-client: update-entity-sorted |
392.31 ops/s (± 6.5%) |
384.62 ops/s (± 3.2%) |
0.98 |
data-client: update-entity-multi-view |
384.62 ops/s (± 8.4%) |
322.58 ops/s (± 6.0%) |
0.84 |
data-client: list-detail-switch-10 |
12.45 ops/s (± 8.1%) |
11.87 ops/s (± 6.3%) |
0.95 |
data-client: update-user-10000 |
101.02 ops/s (± 3.5%) |
99.01 ops/s (± 4.8%) |
0.98 |
data-client: invalidate-and-resolve |
51.28 ops/s (± 4.8%) |
50.76 ops/s (± 3.7%) |
0.99 |
data-client: unshift-item |
322.58 ops/s (± 0.0%) |
333.33 ops/s (± 6.3%) |
1.03 |
data-client: delete-item |
454.55 ops/s (± 6.0%) |
416.67 ops/s (± 0.0%) |
0.92 |
data-client: move-item |
263.16 ops/s (± 3.0%) |
227.27 ops/s (± 3.3%) |
0.86 |
This comment was automatically generated by workflow using github-action-benchmark.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3879 +/- ##
=======================================
Coverage 98.10% 98.11%
=======================================
Files 153 153
Lines 2906 2913 +7
Branches 565 565
=======================================
+ Hits 2851 2858 +7
Misses 11 11
Partials 44 44 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Benchmark
Details
| Benchmark suite | Current: 2a50525 | Previous: 98a7831 | Ratio |
|---|---|---|---|
normalizeLong |
448 ops/sec (±1.23%) |
445 ops/sec (±2.41%) |
0.99 |
normalizeLong Values |
416 ops/sec (±0.16%) |
415 ops/sec (±0.25%) |
1.00 |
denormalizeLong |
301 ops/sec (±2.75%) |
300 ops/sec (±2.18%) |
1.00 |
denormalizeLong Values |
269 ops/sec (±2.32%) |
274 ops/sec (±2.20%) |
1.02 |
denormalizeLong donotcache |
1021 ops/sec (±0.14%) |
1027 ops/sec (±0.40%) |
1.01 |
denormalizeLong Values donotcache |
760 ops/sec (±0.18%) |
761 ops/sec (±0.31%) |
1.00 |
denormalizeShort donotcache 500x |
1570 ops/sec (±0.64%) |
1584 ops/sec (±0.61%) |
1.01 |
denormalizeShort 500x |
849 ops/sec (±2.24%) |
864 ops/sec (±2.10%) |
1.02 |
denormalizeShort 500x withCache |
7526 ops/sec (±0.13%) |
6266 ops/sec (±0.11%) |
0.83 |
queryShort 500x withCache |
2969 ops/sec (±0.11%) |
2799 ops/sec (±0.19%) |
0.94 |
buildQueryKey All |
55104 ops/sec (±0.21%) |
55711 ops/sec (±0.17%) |
1.01 |
query All withCache |
6033 ops/sec (±0.19%) |
6132 ops/sec (±0.30%) |
1.02 |
denormalizeLong with mixin Entity |
284 ops/sec (±2.35%) |
285 ops/sec (±2.01%) |
1.00 |
denormalizeLong withCache |
8158 ops/sec (±0.20%) |
7880 ops/sec (±0.25%) |
0.97 |
denormalizeLong Values withCache |
5101 ops/sec (±0.11%) |
5100 ops/sec (±0.12%) |
1.00 |
denormalizeLong All withCache |
5844 ops/sec (±0.13%) |
5871 ops/sec (±0.67%) |
1.00 |
denormalizeLong Query-sorted withCache |
6057 ops/sec (±0.18%) |
6159 ops/sec (±0.26%) |
1.02 |
denormalizeLongAndShort withEntityCacheOnly |
1864 ops/sec (±0.19%) |
1786 ops/sec (±0.23%) |
0.96 |
denormalize bidirectional 50 |
5808 ops/sec (±1.85%) |
5943 ops/sec (±1.89%) |
1.02 |
denormalize bidirectional 50 donotcache |
40596 ops/sec (±0.97%) |
40836 ops/sec (±0.64%) |
1.01 |
getResponse |
4576 ops/sec (±0.60%) |
4791 ops/sec (±0.53%) |
1.05 |
getResponse (null) |
11060843 ops/sec (±0.94%) |
10513376 ops/sec (±0.93%) |
0.95 |
getResponse (clear cache) |
267 ops/sec (±1.89%) |
274 ops/sec (±1.87%) |
1.03 |
getSmallResponse |
3619 ops/sec (±0.08%) |
3370 ops/sec (±0.07%) |
0.93 |
getSmallInferredResponse |
2763 ops/sec (±0.08%) |
2572 ops/sec (±0.11%) |
0.93 |
getResponse Collection |
4464 ops/sec (±0.35%) |
4588 ops/sec (±0.42%) |
1.03 |
get Collection |
4583 ops/sec (±0.28%) |
4708 ops/sec (±0.26%) |
1.03 |
get Query-sorted |
5202 ops/sec (±0.15%) |
5380 ops/sec (±0.25%) |
1.03 |
setLong |
448 ops/sec (±0.41%) |
462 ops/sec (±0.16%) |
1.03 |
setLongWithMerge |
249 ops/sec (±0.49%) |
257 ops/sec (±0.21%) |
1.03 |
setLongWithSimpleMerge |
271 ops/sec (±0.23%) |
274 ops/sec (±0.15%) |
1.01 |
setSmallResponse 500x |
928 ops/sec (±0.14%) |
940 ops/sec (±0.10%) |
1.01 |
This comment was automatically generated by workflow using github-action-benchmark.
Made-with: Cursor
Made-with: Cursor
Summary
getCacheKeyinGlobalCache.getEntity: Avoids eagerly creating bothlocalCacheandcycleCacheMaps per entity type on every access. ThecycleCacheis now only created when actually entering the denormalize path (lazy initialization). Also replacespush(...spread)with an indexedfor-loop when copying cached entity dependencies to avoid spread operator overhead._removeSchemainCollection.CreateMover: Eliminates per-callObject.createinnormalizeMove, removing a source of hidden class polymorphism that caused V8 "wrong call target" deoptimizations. TheaddSchemais also eliminated sincethis.mergeis already theaddMergefunction.These optimizations target the
move-itemandupdate-user-10000React benchmark scenarios identified via--trace-opt --trace-deoptprofiling.Test plan
move-itemandupdate-user-10000React benchmarks show no regressionMade with Cursor
Note
Medium Risk
Moderate risk: changes touch core normalization/denormalization cache behavior and collection membership updates; intended semantics should be preserved but regressions would be subtle and performance-focused.
Overview
Reduces allocations in hot normalization/denormalization paths.
In
Collection.CreateMover/normalizeMove, pre-creates and reuses a_removeSchemaand stops creating per-calladdSchema/removeSchema, reusingthisfor add merges while still using the prebuilt remove schema.In
GlobalCache.getEntity, lazily initializes the per-entity-typecycleCacheonly when entering the compute/denormalize branch, and replacesdependencies.push(...deps)with an indexed loop to avoid spread overhead.Reviewed by Cursor Bugbot for commit 2a50525. Bugbot is set up for automated code reviews on this repo. Configure here.