Skip to content

perf(gc): inline hot-TLS values in HotTls; leaf write-barrier entry (ECS round 4, +2.7%) - #8935

Merged
proggeramlug merged 5 commits into
PerryTS:mainfrom
proggeramlug:perf/ecs-r4-gc-tls
Aug 28, 2026
Merged

perf(gc): inline hot-TLS values in HotTls; leaf write-barrier entry (ECS round 4, +2.7%)#8935
proggeramlug merged 5 commits into
PerryTS:mainfrom
proggeramlug:perf/ecs-r4-gc-tls

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Two GC/TLS mechanisms from the ECS round-4 chain (each screened with paired alternating runs on the idle Mac mini and confirmed over 15 pairs on the codehz/ecs "5k entities: 3 commands each + sync" row). They were pushed onto #8916 after it had already merged with only its first commit, so they never reached main — this PR re-cuts them from current main. Write-up: secret-tests/ecs-suite/PERRY_ECS_FOLLOWUP_2026-08-27_CLAUDE.md.

step control result 9-pair screen 15-pair confirm
inline hot-TLS values in HotTls 3.545 ms 3.503 ms +1.18%, 9/9 +1.17%, 15/15, 30 oracles (r4h-confirm.json)
leaf write-barrier entry 3.505 ms 3.450 ms +1.59%, 9/9 +1.56%, 15/15, 30 oracles (r4i-confirm.json)
  • runtime: the hottest small thread-local values live inline in HotTls. A hot-TLS slot and a named pointer field both resolve as TSD base → HotTls → slot pointer → value; PC histograms put the remaining self time of the write barrier, js_map_clear (10k calls/frame, both hot offsets on its two TLS probes), is_registered_box_ptr and array_prototype_addr on that dependent chain rather than on anything they compute. Small Copy values with a const initial state can live in HotTls itself (TSD base → HotTls → value), so the barrier's one-entry dirty-page cache, the memoized Array.prototype/Object.prototype rows and the three box-pointer caches now do; the generic slot mechanism is unchanged for everything else, the collector's root rewrite of the prototype rows walks the inline cells exactly as it walked the slot, and the custody audit's frontier entries for the removed box-cache statics are deleted (the deletion is the receipt).
  • gc: the write barrier's dirty-page hit returns from a leaf entry. Every pointer store into an old object reached js_write_barrier_slot_validated_parent, which made two out-of-line calls before anything was decided — decode_heap_addr for the child, and incremental_mark_barrier_value, whose "no cycle anywhere" test sat inside the callee — and then entered the outlined write_barrier_decoded_parent (six callee-saved registers) to run the one-entry dirty-page compare that answers the second and third push into the same bucket. The tag decode and the idle test now inline (their slow arms are cold, out of line), and the cache test is hoisted into the entry ahead of the outlined body (gc/barrier/leaf.rs), so a hit is a leaf path. Counters and the remembered set built are unchanged; pinned by validated_parent_entry_answers_a_cached_dirty_page_store_before_the_body.

Tests: runtime suite (2764) incl. gc::tests::barrier (411, serial), runtime_roots, box::; custody audit + lint gates and merge-base ratchets replayed locally.

https://claude.ai/code/session_01FUvFrRNZyc5qknBiJbYbby

Summary by CodeRabbit

  • Performance

    • Improved runtime and garbage-collection performance by optimizing frequently accessed thread-local values.
    • Accelerated write-barrier handling for cached dirty-page cases.
    • Benchmarks show improvements of up to 1.6% in relevant workloads.
  • Bug Fixes

    • Added regression coverage for validated-parent write-barrier behavior, including cached-page handling and edge cases.

Ralph Küpper added 4 commits August 28, 2026 11:17
…otTls

A hot-TLS slot and a named pointer field both resolve as TSD base → HotTls →
slot pointer → value. On the three probes that run on nearly every store or
boxed read — the write barrier's one-entry dirty-page cache, the memoized
Array.prototype / Object.prototype rows consulted by every indexed array
write, and the direct-mapped box-pointer caches — the profile put the
barrier's remaining self time on that dependent chain rather than on
anything it computed. Small Copy values with a const initial state can live
in HotTls itself (TSD base → HotTls → value), so these five now do; the
generic slot mechanism is unchanged for everything else, and the collector's
root rewrite of the prototype rows walks the inline cells exactly as it
walked the slot.

Claude-Session: https://claude.ai/code/session_01FUvFrRNZyc5qknBiJbYbby
Every pointer store into an old object reached js_write_barrier_slot_validated_parent,
which made two out-of-line calls before anything was decided — decode_heap_addr
for the child, and incremental_mark_barrier_value, whose "no cycle anywhere"
test sat inside the callee — and then entered the outlined
write_barrier_decoded_parent, whose frame saves six registers, to run the
one-entry dirty-page cache compare that answers the second and third push
into the same bucket. The tag decode and the idle test now inline (their
slow arms are cold, out of line), and the cache test is hoisted into the
entry ahead of the outlined body, so a hit is a leaf path: a tag test, two
static loads, the hot-TLS page compare, return. The counters and the
remembered set built are unchanged; the decoded-parent body keeps its own
copy of the test for its other callers.

Claude-Session: https://claude.ai/code/session_01FUvFrRNZyc5qknBiJbYbby
@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 8 minutes.

View limit details

Limit details: You’ve used all 8 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b57326db-352b-405a-81c3-2f48787929cb

📥 Commits

Reviewing files that changed from the base of the PR and between feb11f3 and 356ac16.

📒 Files selected for processing (2)
  • crates/perry-runtime/src/gc/barrier/leaf.rs
  • crates/perry-runtime/src/gc/barrier/mod.rs

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ffc4f143-0821-4d37-8c3f-5f9cdc6134b2

📥 Commits

Reviewing files that changed from the base of the PR and between 49279e9 and feb11f3.

📒 Files selected for processing (10)
  • changelog.d/8935-inline-hot-tls-leaf-barrier.md
  • crates/perry-runtime/src/array/prototype_addr.rs
  • crates/perry-runtime/src/box.rs
  • crates/perry-runtime/src/gc/barrier/leaf.rs
  • crates/perry-runtime/src/gc/barrier/mod.rs
  • crates/perry-runtime/src/gc/barrier_store.rs
  • crates/perry-runtime/src/gc/dirty_page_cache.rs
  • crates/perry-runtime/src/gc/tests/barrier_decoded_parent.rs
  • crates/perry-runtime/src/tls_hot.rs
  • scripts/gc_runtime_root_holders.json
💤 Files with no reviewable changes (1)
  • scripts/gc_runtime_root_holders.json

Included review availability: Your plan provides up to 8 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

The runtime moves prototype, box-pointer, and dirty-page caches into HotTls. GC write barriers add inline cached dirty-page exits and outlined validation helpers. A regression test covers validated-parent cache hits, and metadata removes obsolete box-cache root holders.

Changes

Runtime and GC optimization

Layer / File(s) Summary
Inline HotTls cache storage
crates/perry-runtime/src/tls_hot.rs, crates/perry-runtime/src/array/prototype_addr.rs, crates/perry-runtime/src/gc/dirty_page_cache.rs
HotTls now stores dirty-page, prototype-address, and box-pointer cache cells. Prototype and dirty-page cache access uses these cells directly.
Inline box cache migration
crates/perry-runtime/src/box.rs, scripts/gc_runtime_root_holders.json
Box allocation, release, validation, and test cleanup use inline cache arrays. Obsolete box-cache root-holder entries were removed.
Leaf write-barrier fast paths
crates/perry-runtime/src/gc/barrier/leaf.rs, crates/perry-runtime/src/gc/barrier/mod.rs, crates/perry-runtime/src/gc/barrier_store.rs, crates/perry-runtime/src/gc/tests/barrier_decoded_parent.rs, changelog.d/8935-inline-hot-tls-leaf-barrier.md
Write barriers use cached dirty-page early exits, outlined active-cycle and raw-pointer validation helpers, and a regression test for repeated stores on one remembered page.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to feb11

The PR introduces localized GC/TLS performance optimizations while preserving the existing barrier controls and remembered-set behavior; no actionable merge-blocking risk remains beyond normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant SlotStore
  participant CachedDirtyPageCheck
  participant DirtyPageCache
  participant DecodedWriteBarrier
  SlotStore->>CachedDirtyPageCheck: Check parent_addr and slot_addr
  CachedDirtyPageCheck->>DirtyPageCache: Read cached dirty page
  DirtyPageCache-->>CachedDirtyPageCheck: Return hit or miss
  alt Cache hit
    CachedDirtyPageCheck-->>SlotStore: Return without outlined barrier
  else Cache miss
    SlotStore->>DecodedWriteBarrier: Invoke decoded-parent barrier
  end
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description gives detailed summaries, benchmark results, implementation details, and test results. However, it does not use the required template sections and omits the Related issue and Checklist… Rewrite the description using the required headings: Summary, Changes, Related issue, Test plan, Screenshots / output, and Checklist. Add n/a or an issue reference under Related issue, include the required test-plan checkboxes, and comple…
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 39 functions across 8 files. (1 skipped: 1 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies both primary optimizations: inline hot-TLS values and the leaf write-barrier entry. The performance context is relevant.
Full details: Docstring Coverage

Explanation

Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 39 functions across 8 files. (1 skipped: 1 unsupported.)

Full details: Description check

Explanation

The description gives detailed summaries, benchmark results, implementation details, and test results. However, it does not use the required template sections and omits the Related issue and Checklist sections.

Resolution

Rewrite the description using the required headings: Summary, Changes, Related issue, Test plan, Screenshots / output, and Checklist. Add n/a or an issue reference under Related issue, include the required test-plan checkboxes, and complete the checklist items explicitly.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…the leaf barrier

Both are `-D warnings` errors, so the `warnings` job was red on this branch.
@proggeramlug
proggeramlug merged commit 4e2ff84 into PerryTS:main Aug 28, 2026
18 checks passed
@proggeramlug

Copy link
Copy Markdown
Contributor Author

Composition check on current main (49279e99e, which already carries #8917/#8921/#8923): a merge of #8933 + #8934 + #8935 onto it, paired alternating 9-pair screen on the idle Mac mini, codehz/ecs "5k entities: 3 commands each + sync": 3.955 → 3.453 ms/op, +12.7%, 9/9 (r4all-screen.json; process oracles 18/18 on both binaries). Each PR was also gated on an isolated perrymaster clone (full runtime/codegen/native-proof/transform suites + lint/ratchet replay): green; the only reds seen were shared-host parallel flakes that pass alone/serial and reproduce on plain main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant