Merge train 180: #10177, #10186 - #10200
Conversation
…ion already holds it JSON results at or above 512 KB are malloc-tracked so that the next minor can reclaim a discarded result without a whole-old-heap trace. That trade inverts when the young generation already holds at least as many bytes as the leaf, which is the shape of a freshly parsed document the caller is about to stringify: a non-empty malloc registry forbids the untraced in-place promotion, so the next minor traces the whole tree (55 ms for a 20 MB document, 52% of the roundtrip's wall time) to reclaim one leaf. Such a leaf is now born old in the arena instead. The tree promotes untraced, and the leaf is reclaimed by the old-reclaim full that has to mark the tree anyway. Results allocated while the young generation is small keep the malloc path, and only tracked leaves charge malloc-output debt. (cherry picked from commit 2b4751b)
(cherry picked from commit 9115323)
…here The O0 machine-pipeline fallback is selected per function but applied per module, so every ordinary function sharing a unit with one extreme generated function is demoted with it. Measured on @babel/parser unit 0 (LLVM 22, x86-64, -Os IR pipeline): the demotion costs its 282 ordinary siblings 2.06 MiB of extra machine code (1.382 -> 3.441 MiB, 168 of 282 functions change) and the offending 227k-instruction closure another 2.0 MB (241 KB -> 2,254 KB), to save 6 s of llc time. LLVM has no per-function escape: optnone reaches instruction selection and the optional machine passes but not LiveIntervals or the greedy allocator, so an optnone-demoted giant still took 9.5 s of the 10.0 s and more memory than the undemoted arm. So raise the ceiling from 100k to 600k instructions -- but only on x86_64, which is where every affordability measurement was made: above the whole measured population (60 of the 61 OpenCode-corpus functions past the old ceiling are under 600k; the largest measured end to end, mime types/other.ts at 522,756 instructions, emits optimized in 704 s at 2.26 GB against 393 s at 2.34 GB demoted, for 13.72 MB of .text against 36.97 MB). aarch64/arm64 and every other unmeasured target keep 100k: both pathological observations (a 100,152-instruction function past ~10 GiB RSS, a 277k one over 16 minutes in LiveIntervals/regalloc) are arm64, are current rather than stale, and sit inside the 600k band. The ceiling follows the target the unit is emitted for, not the host, so a cross-compile to arm64 keeps arm64's ceiling. Also report every over-budget function instead of only the widest, and name in the message how many functions the unit demotes along with it, so a build names the whole set that has to shrink. PERRY_LL_FAST_EMIT_MAX_INSTRS keeps its semantics on every target: =100000 restores the old ceiling exactly, =0 disables the fallback. (cherry picked from commit 140ad1f)
(cherry picked from commit 739c208)
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (18)
📝 WalkthroughWalkthroughThe pull request routes qualifying large JSON leaves through arena allocation, updates GC trigger handling, and expands validation. It also adds target-specific fast-emission budgets, reports all over-budget functions, updates pipeline tests, and bumps the project version. ChangesJSON leaf GC routing
Fast-emission budget handling
Release metadata
Estimated code review effort: 4 (Complex) | ~45 minutes Change: Bug fix · Severity of issue fixed: Medium Sequence Diagram(s)sequenceDiagram
participant JSONStringifier
participant JSONStorageAllocator
participant GC
participant GCTrigger
JSONStringifier->>JSONStorageAllocator: allocate JSON output
JSONStorageAllocator->>GC: inspect young-generation state
GC->>JSONStorageAllocator: choose arena or malloc storage
JSONStorageAllocator->>GC: record old-born leaf when arena storage is used
GCTrigger->>GC: evaluate the next budgeted trigger
GC->>GCTrigger: return nursery scavenge priority or existing trigger
sequenceDiagram
participant Codegen
participant TargetBudget
participant FallbackAnalysis
participant MachineEmitter
Codegen->>TargetBudget: resolve budget for effective target
Codegen->>FallbackAnalysis: inspect module functions
FallbackAnalysis->>Codegen: return sorted fallback records
Codegen->>MachineEmitter: select O0 pipeline when fallback records exist
Suggested reviewers: ✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
Merge train 180: lands #10177 (birth a large
JSON.stringifyresult in the arena when the young generation already holds a document; closes #10169) at head9115323f84and #10186 (raise the machine-pipeline budget on x86-64, keep 100k elsewhere) at head739c208c71, plus the workspace version bump to 0.5.1553.The four PR commits were cherry-picked onto
fc736cbf8a(main after the #10176, #10181 and #10183 landings) without conflicts.Validation (macOS arm64)
Full validation first ran on the same two PRs over
5cec2fbbc9:scripts/run_lint_gates.sh: 80 of 83 pass. The 3 failures are the same steps as main: public benchmark evidence freshness, API docs drift, and-D warningsstopping on main'sglobal_this_webassembly.rsdead code (register_extern_wrapper,registered_extern_handle,wasm_memory_descriptor_maximum).cargo test --release: perry-runtime 3728 passed / 0 failed, perry-codegen 1997 / 0stringify8/8.test_gap_json36 pass, 1 fail,json_lazy_defineproperty_index, which isparity_failingap_snapshot.json(JSON.parse lazy array: Object.defineProperty index accessor is bypassed by reads #10097).PERRY_GC_SCHEDULE_SEED3 and 180 at rate 0.2 with forced evacuation, verification and from-space protection (399–417 copying minors).Confirmation on this exact tree, rebased because #10183 added string allocators next to
json_output_storage_alloc:RUSTFLAGS=-Dwarnings cargo check -p perry --binscargo test --release -p perry-runtime: 3738 passed / 0 failedstringify8/8,regex24/24, andtest_gap_json36 plus the known JSON.parse lazy array: Object.defineProperty index accessor is bypassed by reads #10097 failureCI attribution (vs main's run at
b5a82cfeae)native_stackfailure, the same four gated gap regressions with identical output, and the same gc-stress failure.cargo-testpassed.Review notes
json_output_storage_allocnow charge malloc-output debt only for tracked leaves. The one-shotGC_YOUNG_LEAF_BORN_OLDflag is consumed bygc_budgeted_due_triggerwhatever it decides, so it cannot starve old-reclaim.x86_64/amd64targets get the 600k ceiling. aarch64 and every unmeasured target keep 100k, keyed on the target the unit is emitted for, andPERRY_LL_FAST_EMIT_MAX_INSTRSstill overrides everywhere.Summary by CodeRabbit
Performance
Documentation
Release