Skip to content

Beat JVM Clojure across the board on CPU and memory - #225

Open
m-hu wants to merge 10 commits into
masterfrom
perf/h2h-cpu-mem
Open

m-hu wants to merge 10 commits into
masterfrom
perf/h2h-cpu-mem

Conversation

@m-hu

@m-hu m-hu commented Nov 3, 2026 •

Copy link
Copy Markdown

Summary

clojure-rs now beats JVM Clojure across the board: 23/23 benchmark cases win on wall time, CPU time, and peak RSS (head of hhh-pa/clojure master incl. PR222 + this branch, vs head of clojure/clojure master, release builds, medians of 2 JVM / 3 RS reps).

Branch: perf/h2h-cpu-mem (9 commits on current master). No Maniphest task or spec doc exists for this work — the acceptance criterion is the head-to-head result table in the Test Plan.

Problems

Profiled head-to-head runs (/tmp/h2h, since removed — numbers below) showed clojure-rs behind JVM Clojure on CPU in most cases and on memory in several, e.g. vec-conj 44s (quadratic packed-conj copies), reduce-range 11.3s (per-element seq dispatch over ranges), arith-loop-big 12s (interpreter dispatch per round), map-build-big 2.7s + 255MB, bubble-big 4.0s vs 1.4s (rpds path-copy per assoc), map-filter-big 1.59s vs 0.84s (lazy-thunk stepping + Arc churn), atom-doseq-big 1.29s vs 1.02s.

Root causes

  • Packed int vectors copied the whole run per mutating op when shared; generic trie path-copied plus one Arc per leaf.
  • Range seqs stepped through generic first/next dispatch with fresh tails per next.
  • Counted loop/recur bodies paid full interpreter dispatch, Env frames, and Value clones per round.
  • map/filter chains over ranges allocated/stepped a lazy thunk per element with mutex + memoization traffic.
  • swap! locked twice per iteration, built an arg Vec, and generically applied f.
  • reduce cloned accumulator + head per element and dispatched + through generic invoke.

Solutions (one per cause, each fails closed to the interpreter)

  1. Chunked packed vectors (value.rs): frozen 64-item chunks in an rpds spine + tail → O(1) persistent conj/assoc at ~8B/elem; shared big vecs unpack once.
  2. Range fast paths (seq.rs, seqs.rs): in-place next_head, chunk tails materialized once, walk_range head-at-a-time walk with the same inexact-raise point.
  3. Counted-loop accelerator (eval.rs try_int_loop): shape-matched if+recur loops run tagless i64 rounds; overflow, exotic values, and root-epoch changes bail to the exact interpreter behavior.
  4. Map threading (value.rs insert_threaded/deep_clone): loop-carried maps assoc in place on a detached copy; pristine assoc-root check + per-round epoch guard since assoc observes redefinition.
  5. Arithmetic inlining (expr.rs/eval.rs): +/</inc etc. link directly, but unlike the JVM's :inline arithmetic every inlined call observes redefinition (plan guard + per-round epoch re-check, pinned in int_loop.rs); everything else stays indirect (probed: JVM observes redefinition for conj/pr/apply).
  6. Closure call fast paths: local-head calls skip generic dispatch (still funneled through the guarded apply_closure — depth guard + conveyed bindings preserved); single-arg frames bind inline with in-place recur rebind.
  7. Seq fusion (lazy.rs/seqs.rs): uniquely-owned map/filter/remove chains over ranges walk fused in 32-rounds — same heads, order, per-chunk checks, and side-effect interleaving as stepping; shared/forced cells decline (peeled shells rebuilt fresh).
  8. swap! int fusion (concurrency.rs): known arith natives over int state publish under one lock; validators/watches/non-int/overflow take the generic CAS loop.
  9. Vector-threaded loops: packed-int loop vars thread through assoc_int_threaded (copy-on-write chunks, fresh entry identity); steps cover multi-pair assoc, nth reads, and ifs over int comparisons. assoc/nth roots verified pristine + epoch re-check; OOB/overflow/generic vectors bail exactly.
  10. Fused (reduce + init? range) (seqs.rs): tagless i64 accumulation.

No regressions

Every fast path declines to the slow path on anything it doesn't model (non-int values, overflow → long overflow like the JVM, out-of-bounds, 3-arg nth, nested assoc chains, mixed-base steps, redefined roots, validators/watches, shared cells). New pins: int_loop.rs, map_loop.rs, vec_loop.rs, seq_peel.rs, swap_fast.rs, local_apply.rs, direct.rs, packed.rs, map_threaded.rs — including peeled-vs-generic parity, chunk-boundary effect order, entry preservation/identity, bail-error identity, and redefinition observance. Full suite gate green (see Test Plan).

Test Plan

  • cargo fmt --all --check — clean.
  • cargo clippy --locked --no-default-features -p clj-eval --all-targets — no errors/warnings.
  • Full repo gate (/tmp/h2h/gate.sh, chunked per-suite cargo test over all clj-eval/clj-value/workspace suites incl. the 9 new/changed pin files above) — GATE_FAIL=0.
  • Release build (-p clojure-rs) + head-to-head vs JVM master, medians, rs_wall/jvm_wall:
    • small (12/12 wall+CPU+RSS wins, 0.1–0.4x CPU): coldstart, arith-loop 0.33s, reduce-range 0.24s, map-filter 0.31s, fib 0.26s, vec-conj 0.34s, vec-transient 0.27s, map-build 0.41s, str-build 0.23s, sort-int 0.24s, bubble 0.26s, atom-doseq 0.43s.
    • big (11/11 wall+CPU+RSS wins): arith-loop-big 0.81 vs 1.01, atom-doseq-big 0.93 vs 1.04, bubble-big 0.49 vs 1.42, fib-big 0.76 vs 0.99, map-build-big 0.97 vs 1.25, map-filter-big 0.59 vs 0.82, reduce-range-big 0.29 vs 0.77, sort-int-big 0.35 vs 1.13, str-build-big 0.33 vs 0.78, vec-conj-big 0.62 vs 1.02, vec-transient-big 0.47 vs 0.99.
    • Memory extremes: bubble-big RSS 0.05x (25MB vs 505MB); thinnest RSS margins map-build-big 0.82x, str-build-big 0.84x.
  • Diffstat: 20 files, +4165/−149 (of which ~1400 lines are new regression pins).

AI-Tool: Muse Code powered by Meta Muse Spark

@m-hu
m-hu requested a review from michael-h-hu November 3, 2026 18:58
@m-hu
m-hu force-pushed the perf/h2h-cpu-mem branch from d83f4f1 to 57cbd34 Compare November 3, 2026 19:33
@m-hu

m-hu commented Nov 3, 2026

Copy link
Copy Markdown
Author

Update: rebased onto master (decf09b8, PR #224) and re-signed all commits with the registered id_commit_sign SSH key (the previous push used mhh_commit_sign, which GitHub reports as unknown_key).

Conflict resolution (value.rs only, 2 commits): PR #224 introduced owned vector APIs (set_nth_owned/drop_last_owned) on flat packed runs; merged with this branch's chunked PackedInts by keeping the owned shape and implementing the packed arms via one-chunk-copy set_nth/drop_last plus unpack_packed (the PACKED_COW_LIMIT step is subsumed by chunking, as in the original series). Plus one clippy fixup on the resolution.

Re-verified after rebase: cargo fmt --check clean, cargo clippy clean, full suite gate GATE_FAIL=0, release rebuild, and full small+big h2h re-run — still 23/23 wins on wall/CPU/RSS (e.g. bubble-big 0.49s vs 1.42s, reduce-range-big 0.30s vs 0.74s).

AI-Tool: Muse Code powered by Meta Muse Spark

@m-hu
m-hu force-pushed the perf/h2h-cpu-mem branch from 1a092bc to 9c60bfc Compare November 3, 2026 21:56
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Coverage report: 100.00% (lines)

Threshold 100%: PASS (enforced by the quality gates).

File Lines Hit %%
rust/crates/clj-aot/src/driver.rs 31 31 100.00%
rust/crates/clj-aot/src/jit.rs 156 156 100.00%
rust/crates/clj-aot/src/lib.rs 72 72 100.00%
rust/crates/clj-aot/src/link.rs 246 246 100.00%
rust/crates/clj-aot/src/lower.rs 785 785 100.00%
rust/crates/clj-aot/src/target.rs 48 48 100.00%
rust/crates/clj-core/src/lib.rs 3 3 100.00%
rust/crates/clj-eval/src/aot_subset.rs 2027 2027 100.00%
rust/crates/clj-eval/src/arrays.rs 1123 1123 100.00%
rust/crates/clj-eval/src/async.rs 1125 1125 100.00%
rust/crates/clj-eval/src/builtin_expand.rs 358 358 100.00%
rust/crates/clj-eval/src/classemu.rs 3580 3580 100.00%
rust/crates/clj-eval/src/coll.rs 2090 2090 100.00%
rust/crates/clj-eval/src/collmisc.rs 784 784 100.00%
rust/crates/clj-eval/src/compiler.rs 211 211 100.00%

Lowest 15 of 99 files. Full HTML report: download the coverage-report artifact.

Updated by the quality gates run on every push.

@codecov

codecov Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

m-hu added a commit that referenced this pull request Nov 4, 2026
Port the integer-loop accelerator onto the DirectNative master (#226):
counted `loop`s over `if`/comparison/`recur` match only validated
`Expr::DirectNative` sites (arith/compare ride the `FastArith` tag;
`assoc`/`nth`/`=` ride var identity plus the analyzed root's display),
and the plan guard replays `direct_valid` per inlined call (no thread
binding, fresh epoch, `Arc::ptr_eq` root, with the slow road's cache
fill) plus a per-round epoch re-check. Every inlined call observes
redefinition exactly like the evaluator's own direct calls.

Integer steps compile to postfix ops evaluated with shape-specialized
fast paths (leaves and leaf-built steps compute directly; nested runs
the iterative machine — no recursion, deeply nested source can't
overflow the native stack). Map/vector steps thread one assoc chain
through the in-place APIs. Exit branches evaluate via the interpreter
against one reused env (rebound in place when unobserved), and a bail
after an exit ran resumes the slow loop from the current state instead
of rerunning from entry (rerunning would repeat its effects — caught
by future_stress_swap: a concurrent `def` bumped the epoch mid-loop).

Peeled `map`/`filter` over ranges apply plain closures through a
reused frame (`apply_reused`: one arity resolution, in-place rebinds,
same depth guard and `recur`-to-fn looping as `apply_closure`).
Local-head closure calls skip generic dispatch (`local_closure`).

h2h vs JVM Clojure master: 23/23 wins across wall, CPU, and peak RSS
(small 12/12, big 11/11).

Tests: int_loop/map_loop/vec_loop suites (observance, overflow,
capture, bail-resume, exotic reseed), seq_peel reused-frame pins
(capture, recur-to-fn, depth parity, ineligible fns), eval unit pins.
@m-hu
m-hu force-pushed the perf/h2h-cpu-mem branch 2 times, most recently from e3481e0 to 8664c00 Compare November 4, 2026 11:31
m-hu added a commit that referenced this pull request Nov 4, 2026
Port the integer-loop accelerator onto the DirectNative master (#226):
counted `loop`s over `if`/comparison/`recur` match only validated
`Expr::DirectNative` sites (arith/compare ride the `FastArith` tag;
`assoc`/`nth`/`=` ride var identity plus the analyzed root's display),
and the plan guard replays `direct_valid` per inlined call (no thread
binding, fresh epoch, `Arc::ptr_eq` root, with the slow road's cache
fill) plus a per-round epoch re-check. Every inlined call observes
redefinition exactly like the evaluator's own direct calls.

Integer steps compile to postfix ops evaluated with shape-specialized
fast paths (leaves and leaf-built steps compute directly; nested runs
the iterative machine — no recursion, deeply nested source can't
overflow the native stack). Map/vector steps thread one assoc chain
through the in-place APIs. Exit branches evaluate via the interpreter
against one reused env (rebound in place when unobserved), and a bail
after an exit ran resumes the slow loop from the current state instead
of rerunning from entry (rerunning would repeat its effects — caught
by future_stress_swap: a concurrent `def` bumped the epoch mid-loop).

Peeled `map`/`filter` over ranges apply plain closures through a
reused frame (`apply_reused`: one arity resolution, in-place rebinds,
same depth guard and `recur`-to-fn looping as `apply_closure`).
Local-head closure calls skip generic dispatch (`local_closure`).

h2h vs JVM Clojure master: 23/23 wins across wall, CPU, and peak RSS
(small 12/12, big 11/11).

Tests: int_loop/map_loop/vec_loop suites (observance, overflow,
capture, bail-resume, exotic reseed), seq_peel reused-frame pins
(capture, recur-to-fn, depth parity, ineligible fns), eval unit pins.
@m-hu
m-hu force-pushed the perf/h2h-cpu-mem branch from 4cff405 to 2bc5372 Compare November 4, 2026 12:33
m-hu added a commit that referenced this pull request Nov 4, 2026
Port the integer-loop accelerator onto the DirectNative master (#226):
counted `loop`s over `if`/comparison/`recur` match only validated
`Expr::DirectNative` sites (arith/compare ride the `FastArith` tag;
`assoc`/`nth`/`=` ride var identity plus the analyzed root's display),
and the plan guard replays `direct_valid` per inlined call (no thread
binding, fresh epoch, `Arc::ptr_eq` root, with the slow road's cache
fill) plus a per-round epoch re-check. Every inlined call observes
redefinition exactly like the evaluator's own direct calls.

Integer steps compile to postfix ops evaluated with shape-specialized
fast paths (leaves and leaf-built steps compute directly; nested runs
the iterative machine — no recursion, deeply nested source can't
overflow the native stack). Map/vector steps thread one assoc chain
through the in-place APIs. Exit branches evaluate via the interpreter
against one reused env (rebound in place when unobserved), and a bail
after an exit ran resumes the slow loop from the current state instead
of rerunning from entry (rerunning would repeat its effects — caught
by future_stress_swap: a concurrent `def` bumped the epoch mid-loop).

Peeled `map`/`filter` over ranges apply plain closures through a
reused frame (`apply_reused`: one arity resolution, in-place rebinds,
same depth guard and `recur`-to-fn looping as `apply_closure`).
Local-head closure calls skip generic dispatch (`local_closure`).

h2h vs JVM Clojure master: 23/23 wins across wall, CPU, and peak RSS
(small 12/12, big 11/11).

Tests: int_loop/map_loop/vec_loop suites (observance, overflow,
capture, bail-resume, exotic reseed), seq_peel reused-frame pins
(capture, recur-to-fn, depth parity, ineligible fns), eval unit pins.
@m-hu
m-hu force-pushed the perf/h2h-cpu-mem branch from 9cffae5 to 0e8627b Compare November 4, 2026 14:10

@michael-h-hu michael-h-hu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 1 review of PR #225 (as michael-h-hu)

Inspected: full diff vs master (origin/master..HEAD, 20 files, ~5.7k lines), all 9 commit messages, PR body, and gh pr checks (all green: quality gates incl. 100% line + patch coverage, 8/8 shards, msrv, loom, fmt+clippy). Locally verified: all 9 commits carry Good id_commit_sign signatures; every st.root = write site in clj-eval goes through an epoch-bumping wrapper (loop-guard soundness vs cross-thread redefinition); re-ran the h2h on the tip — 23/23 cases win on wall+CPU+RSS.

No correctness defects found in the audited hot paths (try_rebind merge, set_nth_owned tuple match, plus_range empty-sum fold, plan guard + epoch re-check, peel recur_pool recycle, run_loop_accel extraction). Two findings below.

High: the branch replaces #234's packing instead of building on it

value.rs deletes MAX_PACKED_LEN (1024, single-run cap from #234, just landed to fix quadratic conj/assoc chains) and substitutes chunked-64 packing, and 1ac12e6c re-pins #234's cap tests to chunked equivalents. Meanwhile #238's measured gains were taken on #234 packing, so this new combination (chunked packing × pooled recur × interp sweep) has no proxy comparison against the master it replaces.

The in-commit rationale (O(1) updates at any size vs cap+generic fallback) is coherent and the h2h wins are verified, so this is not a correctness block — but it needs an explicit decision before landing, one of:

  1. Adopt #234's packing on this branch and re-verify the h2h table, or
  2. Keep chunked packing and file a follow-up issue to reconcile/unify the two packing designs with a proxy comparison (quadratic-chain workloads from #234 plus the #238 proxies), linked from the PR.

Low: PR body is stale in two places

  • It says "16 commits"; the branch now has 9 after the rebases.
  • Point 5 under Solutions ("Arithmetic inlining … :inline only … JVM ignores redefinition for these") contradicts the landed design — the accelerator observes redefinition for every inlined call (unlike JVM :inline), as pinned in int_loop.rs. Please update the body.

Residual risks (non-blocking, noted for the record)

  • Narrowest h2h margins are map-build-big (0.82x) and str-build-big (0.83x) RSS — still clear wins, but the ones to watch on Kroki variance.
  • This review was produced in the same session as the implementation (self-review): it covers mechanical verification above, not an independent second pair of eyes on the 5.7k-line design. Worth a human skim of try_int_loop's matcher/guard interplay before landing anything this perf-critical.

AI-Tool: Muse Code (Meta Muse Spark)

@m-hu

m-hu commented Nov 4, 2026

Copy link
Copy Markdown
Author

Addressed round-1 findings (m-hu)

  • High (Cap packed Int runs at 1024 to fix #227 case regressions #234 packing divergence) → kept chunked-64 packing (option 2): O(1) persistent updates at any size with ~8B/elem density vs cap-then-generic-fallback, verified by the 23/23 h2h wins and the full suite at 100% line coverage. Filed the reconcile/unify follow-up with proxy comparisons as Reconcile chunked-64 packing (PR #225) with #234 single-run packing #240 and pointed at it from the PACKED_CHUNK docs (7459bd37).
  • Low (stale PR body) → fixed: commit count corrected (16 → 9) and point 5 rewritten to describe the landed observant-inlining design (every inlined call observes redefinition via the plan guard + per-round epoch re-check), matching int_loop.rs.

Gates for the touched area: cargo fmt --check clean, cargo clippy -p clj-value --all-targets zero warnings, packed 30/30 green. Watching PR checks to green.

AI-Tool: Muse Code (Meta Muse Spark)

@michael-h-hu michael-h-hu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 2 review of PR #225 (as michael-h-hu) — approve

Re-verified round-1 findings against the new tip (7459bd37):

  • High (#234 packing divergence) → resolved: author kept chunked-64 with the recorded rationale (O(1) updates at any size, h2h-verified), filed the reconcile/unify follow-up as #240 (open, correctly scoped with proxy comparisons), and pointed at it from the PACKED_CHUNK docs. Acceptable: the design decision is explicit, tested, and tracked.
  • Low (stale PR body) → resolved: commit count corrected, point 5 now describes the landed observant-inlining design, matching int_loop.rs.

Also confirmed: all PR checks green on the new tip (quality gates incl. 100% line + patch coverage, shards, msrv, loom, fmt+clippy; only tla model check skipping as usual), the new commit is signed, and the shard 2/8 failure on the intermediate run was a runner disk-full flake unrelated to the change (rerun green).

Residual risks (unchanged from round 1): narrowest h2h margins on map-build-big/str-build-big RSS (~0.82x); this loop is a self-review — a human skim of the loop-accelerator matcher/guard interplay is still worthwhile before landing perf-critical code of this size.

AI-Tool: Muse Code (Meta Muse Spark)

m-hu added 10 commits November 4, 2026 22:43
Rebase onto 24c8c74 (#226): carry the non-call-dispatch areas
forward (packed chunked vectors, range stepping/walking, fused
plus-range reduce, map/filter peel chains, swap! fusion, direct
Int compare, Inline recur rebind, macro-expansion helper).
Call dispatch stays on master's Expr::DirectNative; the loop
accelerator rebase follows separately.
Port the integer-loop accelerator onto the DirectNative master (#226):
counted `loop`s over `if`/comparison/`recur` match only validated
`Expr::DirectNative` sites (arith/compare ride the `FastArith` tag;
`assoc`/`nth`/`=` ride var identity plus the analyzed root's display),
and the plan guard replays `direct_valid` per inlined call (no thread
binding, fresh epoch, `Arc::ptr_eq` root, with the slow road's cache
fill) plus a per-round epoch re-check. Every inlined call observes
redefinition exactly like the evaluator's own direct calls.

Integer steps compile to postfix ops evaluated with shape-specialized
fast paths (leaves and leaf-built steps compute directly; nested runs
the iterative machine — no recursion, deeply nested source can't
overflow the native stack). Map/vector steps thread one assoc chain
through the in-place APIs. Exit branches evaluate via the interpreter
against one reused env (rebound in place when unobserved), and a bail
after an exit ran resumes the slow loop from the current state instead
of rerunning from entry (rerunning would repeat its effects — caught
by future_stress_swap: a concurrent `def` bumped the epoch mid-loop).

Peeled `map`/`filter` over ranges apply plain closures through a
reused frame (`apply_reused`: one arity resolution, in-place rebinds,
same depth guard and `recur`-to-fn looping as `apply_closure`).
Local-head closure calls skip generic dispatch (`local_closure`).

h2h vs JVM Clojure master: 23/23 wins across wall, CPU, and peak RSS
(small 12/12, big 11/11).

Tests: int_loop/map_loop/vec_loop suites (observance, overflow,
capture, bail-resume, exotic reseed), seq_peel reused-frame pins
(capture, recur-to-fn, depth parity, ineligible fns), eval unit pins.
#234 capped single-run packed vectors at 1024 ints (past the cap they ride the generic trie) to fix quadratic conj/assoc chains on shared runs. This branch packs Int vectors in 64-item chunks instead, so an update touches one chunk however long the run is - the cap has nothing left to do. Re-pin the four #234 tests to the chunked behavior (long runs stay packed, every version intact).
The quality gate flagged two new violations: eval_int_general (cyclomatic 17) and try_fold_leaves (cyclomatic 22). Split the n-ary leaf folds into per-op helpers and the general-machine match into per-op application plus tiny push/step helpers. No behavior change (same ops, same bail points); nested loop tests cover every new line.
The reused-frame peel path (`apply_reused`, `peeled_closure`,
`pull_peeled_chunk`) only runs under uniquely-owned walks
(`dorun`/`reduce`/friends — `doall` clones, so it always walks
generically); the existing pins all used `doall`, leaving the
reused arms' entry/recur/throw/depth lines uncovered. Mirror them
through `dorun`/`reduce`: ineligible (multi-arity, conveyed,
rest-arg) fns, a retained cross-apply capture, fn `recur` (fresh
and in-place rebinds), a throw, the reused apply's own depth
guard (depth grown purely through peeled self-application), the
peeled inexact-tail raise, and the empty 2-arg `+` reduce bail.

Loop-guard pins: a thread binding on a loop-called var (via a
dynamic alias sharing the `+` native) and a closure redefinition
both decline the plan and compute through the slow path. Leaf
pins: `inc`/`dec` over a zero-operand leaf, a nested-`inc` step,
and a nested-operand `*` fold-mismatch, all computed by the
general machine.

Two provably-dead lines are restructured instead of pinned:
`try_rebind`'s `Patchable` decline becomes an `if let` with
fallthrough to the shared fresh-frame `Err` (`recur` binds fresh
since #171, so no caller rebinds a `Patchable` frame), and
`set_nth_owned` matches (storage, value) once (shape by arm
position) instead of guarding plus re-matching.
`with-redefs` to a closure declines the loop plan through the
pointer-inequality arm (closures are `Extension`-typed), so the
non-`Extension` arm needs a redefinition to plain data: the slow
path calls the data and raises exactly like the interpreter.

A capture held only by the unwound body does not keep the reused
frame observed past the `recur` signal (the `let` frame drops
during unwinding), so the observed-`recur` fresh bind needs a
capture retained outside (an atom), with every round still running
exactly once.

No `RangeValue` constructor yields an empty range (every site
nil-puns empties to `()`), so the empty 2-arg `plus_range` bail
is unreachable: spell the empty sum directly (`(+)` is `0`,
identical to the generic walk's `(f)` call) instead of branching
on it.
Master's pooled recur changed try_rebind to borrow-and-drain
(returning bool): the loop-accelerator exits and the reused-frame
peel apply follow suit. The peel recur landing site recycles its
drained buffer like the slow loop; the entry and exit rebinds
take fresh (never pooled) buffers, so they skip the recycle.
The try_int_loop hook's three-way match pushed eval_loop to
cyclomatic 16. Run the accelerator through a helper (Some
finished, None runs the slow loop) so eval_loop keeps one
branch.
No Clojure-level recur targets a letfn setup frame, so the
Shared-check skip in try_rebind is unreachable through eval
tests; pin the decline (false, values untouched) directly.
Round-1 review decision: keep chunked-64 packing (O(1) updates at
any size, h2h-verified), reconcile/unify with #234's single-run
design in #240 with proxy comparisons.

AI-Tool: Muse Code (Meta Muse Spark)
@m-hu
m-hu force-pushed the perf/h2h-cpu-mem branch from 7459bd3 to 39f8708 Compare November 4, 2026 21:43
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.

2 participants