perf(json): read traced materialized arrays without redundant resolution - #10064
proggeramlug wants to merge 3 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced 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 |
|
Landed on Your commits are on Closing this PR as landed — GitHub cannot auto-close it because the train merges as its own branch. |
The local `lint` tier failed two ratchets that the individual PRs did not run against post-#10064 `main`: raw-handle debt: 962 (baseline 945), 4 per-module violations string payload-access baseline stale (perry-runtime 352 -> 351) The debt is real, not a ceiling question. `--no-raise-vs <merge-base>` refuses a ceiling on a file that was absent at the base and refuses any per-module raise, so `concat_reverse.rs` (0 -> 5) and the new `wide_object_membership_tests.rs` (0 -> 9) could not be listed, and `iterator.rs` (31 -> 32) / `push_pop.rs` (12 -> 14) could not be raised. Converted all 17 sites to the shape the ratchet exists to enforce: - `array/concat_reverse.rs`: both `push(...src)` element loops now read the source inside `with_const_ptr`, so no address survives the `js_array_push_f64` that can move it. The grow path reads the source through `across_const` around a `with_mut_ptr` grow, which is where the post-allocation re-read belongs. - `array/iterator.rs` and `array/push_pop.rs`: `js_array_spread_append` and the generic spread-push take the destination from `across_mut` around the iterator materialization instead of reading it afterwards. - `object/wide_object_membership_tests.rs`: every probe re-reads the rooted receiver. The old shape bound `object.get_raw_mut_ptr()` once and then called `key()` — which allocates a string — repeatedly against that stale address. Debt is back to exactly the recorded 945 with every module inside its ceiling, so neither ratchet file needs an edit. The string payload-access baseline moves the other way: #10070's rewrite of `own_key_present` removed one open-coded `size_of::<StringHeader>()` offset in `object_ops/keys_array.rs`, and that inventory requires recorded progress. One line, 352 -> 351. Claude-Session: https://claude.ai/code/session_017grKysEP6NBPpCiTSBjSZR
#10077 gives a dense array a queue front offset: `capacity` counts the slots remaining AFTER the front, so logical element zero is no longer the end of `ArrayHeader`. That PR converted every open-coded `header + size_of::<ArrayHeader>()` element base in its own tree — but two places on current `main` postdate its branch point and were never converted: - `json_tape/cached_read.rs` (from #10064, landed in train161) open-codes the base on the hot materialized-array read. A materialized JSON array is an ordinary `GC_TYPE_ARRAY` that user code can `shift()`, so after this train that read would return the wrong element. Routed through `crate::array::array_elements_ptr`. - `array/sort.rs` gained four more open-coded bases with the `publish_sorted_values` / `apply_sorted_indices` refactor. Converted with the conflict resolution in the pick itself; only the rustfmt reflow lands here. Also converts the six raw-handle debt sites #10075's two new test modules introduced. `--no-raise-vs <merge-base>` refuses both a ceiling on a module that was absent at the base (`string/slice_tests.rs`) and a per-module raise (`gc/tests/runtime_roots/string_slice.rs`, 1 -> 3), so the sites are converted rather than recorded: - `gc/tests/runtime_roots/string_slice.rs`: the two post-collection re-reads now run the collection inside `across_mut` / `across_const`, and the kept-slice assertions inside `with_const_ptr`. That also retires the module's one pre-existing site, so its ceiling line is DELETED — the deletion is the receipt — and the recorded total falls 945 -> 944. - `string/slice_tests.rs`: the three argument-position reads become `with_const_ptr`, which is the documented shape for a self-rooting entry point such as `js_string_slice`.
Repeated reads from materialized JSON arrays re-entered the general GC header resolver even when their live owner already held the exact traced array edge. The outlined accessor now checks that edge directly, retaining the resolver for growth forwarding and the rooted path for descriptors, holes and construction.
Against frozen R25 on the M1, 1 MB field reads use 15% less CPU and indexed reads use 8–10% less; an independent 11-repetition run reproduces the gains. Full 1/8 MB consumption improves 0.8%/0.7%. Disclosed costs: 20 MB sequential reads have a repeated ~0.5 ns higher median; Unicode stringify has a +3.1% median in the independent recheck. Their observed ranges overlap. Peak retained RSS changes by 0 to +32 KiB.
Validation: 295 serial release JSON tests; all 81 candidate behavior/options checks, positive copying/protected GC witnesses, six matching worker objects and 24 matching native/shadow IR files. Eight archived windows contain 4,196 timed trials, 624 output-verification records and 60 calibration trials. Existing unsuppressed native-root findings, known lazy stringify failures and public benchmark freshness failure remain documented.
The R26 report contains the complete CPU/RSS tables, independent rechecks and source/build fingerprints. Timings use measured source
3aac4d6335da54abeeed73df842decbbe6dd5d71, workspace 0.5.1531, not a release-metadata rebuild or merged main. No GC policy, parse-boundary or lazy-admission change.Stacked on #10052. Ready for review; optimization work continues without waiting for CI. Normal protected checks and merged-main measurement remain before claiming landing.