perf(profile): compile only selected benchmark modes - #361
Conversation
|
This seems a bit too tailored to what the CI bench is exercising now - could we have a more generic solution? |
|
Like in the future what if we want to change what's exercised in each ci bench shard and/or add new shards? |
7df6f5f to
bbe389f
Compare
Documentation blast radius (advisory)These regions may need doc/spec/book updates based on changed paths. Changed files in this PR: 3
|
CI test timing
Run summary
Wall time spans 2 parallel nextest slice shards. Slowest tests
Regressions vs mainNo per-test regressions above the threshold. New slow testsNo new tests ≥30s vs main baseline. |
Benchmark Report
Negative deltas are improvements for time, memory, and proof size. Terminal response component breakdown
The Detailed schedule and proof-size breakdowns by fold level are available in the uploaded |
…378) * ci(profile): improve reports and narrow builds Describe the exact public opening statements and split the compact report into readable timing, memory, and proof size tables. Build each matrix group with its narrow feature while preserving the profile-ci fallback for older merge bases. Fold the current coverage contract into the Book and archive the stale coverage spec. Adapts the useful changes from draft PRs #360, #361, and #362 to the current benchmark matrix. * ci(profile): unify fold report layout Join planner choices, witness geometry, and proof costs in one row per fold. Emit group-specific records for multi-group roots and setup offloads so distinct commitment parameters remain visible. Keep percentage deltas for measured costs and exact merge-base values for discrete planner choices. * fix(profile): correct multi-group report values * feat(profile): compare verifier thread modes * refactor(profile): clarify fold report cells Group planner, work, and proof details into labeled blocks while preserving the side-by-side fold comparison. Keep multi-group precommitment, final-group, and setup-offload parameters visibly separate. * fix(profile): remove persisted bench setup Construct recursive benchmark setup in memory for every sample instead of enabling disk persistence. This prevents public matrix and prefix registry caches from entering per-group handoff artifacts and makes setup timing measure fresh construction. * fix(profile): close benchmark report review gaps * fix(ci): align recursive profile linkage * fix(ci): restore balanced nextest shards
|
Closing this as superseded by #378, merged in e8d63c6. Current main now has six narrow profile feature groups, and profile-ci is their transitive compatibility union. The profile registry compiles only the modes selected for each group. The merged version also reflects the current adaptive profile modes and adds exact coverage and linkage checks. Thank you, Andrii, for the work and for driving this build time improvement. |
…375) * perf(onehot): keep big blocks on the tiled column sweep; row-pass accumulators - Oversized blocks (hot count beyond the wide accumulators' 2^15 headroom) are split into cap-respecting sub-blocks and swept through the bucketed tiled kernel, then re-merged per parent — the previous per-block safe fallback re-streamed n_a A rings per hot coefficient (~15 TB at a 2^26 one-hot trace column group) and dominated trace-scale commits. Measured: 2^26 K=256 jolt prove 333s -> 154s (commit ~252s -> ~80s). - Row-pass accumulation (one wide ring per block live at a time) with the tile budget parameterized; sweep-structure and shift-accumulate microbenches added (ignored) for A/B iteration. - In-place NEON AddAssign/SubAssign for Fp128x8i32 (no temp round-trip; measured neutral on M4 — kept for clarity of intent). - inner_ajtai_wide_onehot_safe retained as the overflow-safety reference. * perf(onehot): fused multi-polynomial commit sweep Every polynomial of a committed group multiplies the same A matrix, but the sweep ran per-poly, re-streaming A once per polynomial — the dominant commit traffic at trace scale (~11 TB at 2^26/K256, ~28 s of the ~82 s commit). Adds a merge-based fused kernel (column_sweep_core_merge): blocks carry cursors over their position-sorted entries and the kernel walks A columns in L1-sized chunks, so one A pass serves every block of every polynomial in the batch — and the counting/scatter pass (whose packed buffer scales with tile size) disappears. Wiring: CommitmentComputeBackend::onehot_commit_rows_multi (default loops the single-poly path), RootCommitKernel::commit_inner_group (default per-source), OneHotPoly::commit_inner_group, and the batched-prove commit path groups the whole batch. Byte-equal to per-poly sweeps (new equality test covers core-vs-core, wrapper round-trip, and tile-size independence); 223 unit tests pass. * perf(onehot): keep merge-sweep accumulator tile and column chunk L1-resident The fused sweep ran its 58-block accumulator set (116 KB) plus a 64 KB widened-column chunk out of L2, costing ~1.5x per accumulate (68 vs 28 ns in the tight-tile bench). Cap the merge tile at 32 blocks of accumulators (64 KB) and halve the column chunk to 16 (32 KB) so both live in L1; the extra A re-streaming this causes is negligible after poly fusion. Also spans the fold grind (fold_grind_sample) for opening attribution. * fix(onehot): self-reduce merge-sweep accumulators at the cap; no block splitting At trace scale every one-hot block exceeds the 2^15 wide-accumulator cap (~2^19 accumulations at ppb 2^21), so the merge path inherited the splitting wrapper's 16x sub-block explosion — which turned the L1 tile cap into a 29x A re-stream and mostly-empty cursor scans (2^26 prove regressed to 252s). Fold each block's wide accumulator into a canonical partial whenever it reaches the cap instead: a handful of reduces per block-row, no splitting, blocks keep dense full-range chunks, and small L1 tiles are genuinely safe. New regression pins the self-reducing kernel against the splitting wrapper on an oversized block. * perf(onehot): bench-tuned merge-sweep defaults (tile 64 blocks, chunk 32 cols) Full (tile x chunk) matrix at a trace-like sparse shape is flat within 5-30%; (64, 32) is the minimum at 61.4 ns/accum vs 64.4 at the previous default. The kernel is at its practical floor for quarter-density one-hot blocks — the 28 ns dense-shape bench figure does not transfer. Adds the merge_sweep_bench tuning matrix (ignored). * feat(onehot): expose clear_block_cache on OneHotPoly The per-block storage is a pure cache over the retained hot indices (blocks_for rebuilds it on demand), but it is trace-scale — ~8 bytes per hot entry across every committed column — and otherwise lives from commit until the opening fold. Callers that know the sweep is done can drop it and pay one parallel rebuild inside the fold. * perf(prover): build the setup NTT envelope slot lazily and let callers drop it between uses — 2^26 jolt e2e peak 87.7->81.3GB, prove 118.8->99.4s Registration at prepare_setup now only reserves the slot cell and records the CRT profile; with_shared_ntt builds single-flight on first use, and drop_built_ntt_slots() returns built slots to the reserved state (next use rebuilds, ~1.9s at the jolt 2^26 envelope). Keeps the ~28GiB transformed matrix out of every window that doesn't touch it: unused setups never build, and the caller can drop between the commit's terminal product and the stage-8 fold. * perf(prover): extent-sized NTT slots + streamed root-relation transforms — 2^26 jolt e2e peak 87.7->55.1GB, prove 118.8->95.6s Three changes that together keep the transformed matrix out of the fold window (it was ~30GiB of standing memory at the jolt 2^26 shape): - with_shared_ntt(extent, f): consumers declare the rows x width extent they slice; slots build at the rounded request (smallest built cover reused, envelope-warmed setups unchanged). The commit's terminal product now builds 5GiB instead of 30. - ring-switch relation/quotient rows above 2^21 rings stream per-element transforms from A's field form inside the tile loop (new cyc-only ring constructor; chunked z-quotient mirrors the cached path's bracketing; both paths bit-identical to the cache fill — equality tests included). The root relation reads each element once per prove, so caching its transform was pure standing memory. - fold-entry envelope warms (prove.rs role/terminal, ring-switch commit_w/commit_terminal_w) skip the base ring dim: its consumers self-serve extent-sized, cross-D dims keep the eager warm. AKITA_NTT_BUILD_BACKTRACE=1 dumps a backtrace per slot build for future residency diagnosis. * perf(prover): drop built NTT slots after the root fold level — fold window 51.3->49.3GiB sampled at 2^26, prove unchanged Root-level slots dwarf every deeper level's; releasing them keeps the fold tail (where sumcheck transients spike) off root-scale caches, and deeper levels rebuild their own sub-GiB slots on first use. Peak-neutral on its own (the stage-3/4 transient now binds at ~51 GiB sampled) — rides with the stage-window items. * perf(prover): releasable setup matrix with seed re-derivation — 2^26 jolt e2e peak 55.1->47.1GB, prove 95.6->99.3s The field-form matrix is a pure function of the public seed, so its backing store is cache. AkitaExpandedSetup.shared_matrix becomes SharedSetupMatrix: seed-backed shape metadata (envelope keys and validation never shrink), covering_at_dyn/full accessors over a swappable Arc store, release_to_prefix, and a per-element MatrixElementDeriver (same XOF stream as derive_public_matrix_flat — bit-identical values, equality-tested). Derivation is captured as monomorphized fn pointers at construction, so consumers need no extra bounds. After the commit sweep (its last full-width reader) the store releases to a 2^21-ring prefix that serves slot rebuilds and small setup reads; wide readers stream per element: the ring-switch relation kernels take a StreamedASource (materialized pre-release, seed-derived after), and the relation weight events read per-column ring slices through SetupRowFamily instead of full row views. Setup-contribution scans cover only the rows they read, not padded view lengths. A full-width covering re-caches the store (the verifier's root-level scan legitimately reads the whole matrix; later proves re-release), so within one process the ~2.4s derive amortizes to once per prove/verify cycle — first-verify cost 0.2->2.6s, standalone verifiers already pay full derivation at setup. * perf(prover): build commit-sweep block tiles lazily from index columns — 2^26 jolt e2e peak 47.1->44.1GB, commit window 43.7->35.1GiB The fused group commit materialized every polynomial's block entries (~200 B/cycle across a 29-poly batch) before sweeping. Blocks partition the ring-element index space contiguously, so from_indices gains block-range variants reading exactly a tile's slice of the retained index columns; per-tile builds across the sweep sum to the same single pass. commit_inner_group hands the sweep LazyOneHotBlocks builders (OneHotCommitBlocks::{SingleChunk,MultiChunk}Lazy) and each thread materializes one L2 tile at a time (~2 GiB in flight for the batch), dropping it after the pass. The tile pass is extracted from the merge kernel and shared, so lazy results are byte-equal to eager (equality-tested). Side effect: later windows drop ~6-9 GiB too — the cache's freed pages no longer linger in the allocator. Single-poly and small-batch paths keep the eager cache. * perf(prover): stream fold blocks from index columns — per-block builds in fold_blocks/_ring, per-(block,position-window) builds in the tensor accumulate The fold's opening-claim evaluation was the sole builder of the full per-block entry cache (probe-verified: every blocks_for build flowed through fold_kernels::evaluate_claims_at_prepared_point), and the tensor decompose reused it. Both consumers visit each entry range exactly once: fold_blocks/_ring build one block at a time, and onehot_accumulate_tensor_lazy builds one (block, position-window) at a time (LazyOneHotBlocks generalized to ring-range builds; pos_in_block stays absolute). Total build work equals one pass over the retained index columns; the eager tensor kernel stays as the reference for the lazy-equality test. 2^26 measurement pending a quiet machine (first attempt voided by mid-run user activity). * refactor: shrink the perf-branch diff before upstreaming - delete measured-dead code: Fp128Lazy (negative A/B result), the row-outer sweep variant + bench entry, inner_ajtai_wide_onehot_safe - delete perf-campaign scaffolding: planner candidate dump, three env-gated backtrace blocks - from_indices delegates to the ring-range builders; one view_layout helper replaces triplicated validation in OneHotPoly; lazy fold arms collapse into a macro; base-dim NTT warm skip moves into ensure_envelope_ntt (one home instead of five call sites) - fix two real residency leaks the review found: the tensor decompose layout probe rebuilt+cached a full block set inside the fold, and two setup-prefix readers materialized the full matrix post-release - export NTT_STREAM_THRESHOLD_RING_ELEMENTS + a pinned release_setup_matrix_to_streaming_prefix so the retained prefix and the streaming threshold cannot drift apart - fix two doc comments stranded on the wrong item by earlier insertions 2^26 validation: prove 101.3s / peak 44.09GB (unchanged). * perf(algebra): fuse fixed-size wide ring shifts * perf(algebra): expose wide ring coefficients * perf(prover): consume decompose witness rows without copies * perf(prover): release root opening storage after fold * fix(profile): propagate commit accumulator bounds * perf(prover): cache only required commit transform * perf(prover): stream CRT-chunked quotient roles * perf(prover): parallelize relation setup columns * perf(prover): coalesce aligned relation events * perf(prover): parallelize exact-prefix folds * fix(prover): preserve group-local relation dimensions * perf(planner): trade payload slack for narrower roots * fix(planner): preserve scalar rank policy through recursion * fix(planner): emit rustfmt-stable catalog imports * fix(planner): remove stale lint expectation * fix(types): size released setup without rebuilding * test(prover): drop local tuning probes * fix(pcs): update releasable setup call sites * test(pcs): align mixed-D cache test with lazy setup * test(prover): split oversized backend test modules * revert(planner): drop payload-slack schedule selection Removes the rank-aware selection policy (payload slack for narrower roots) per review feedback: the planner is being reworked upstream and this policy should not bake into catalog identity. Restores akita-planner, akita-config, and akita-schedules to their origin/main state; the prover optimizations in this branch are unaffected. * fix(ci): repair doc links and glue trait bound Rustdoc denies public docs linking private items; the trusted-artifact glue impl needs RandomSampling for seed re-derived setup construction. * fix(tests): drop tensor-kernel test and port oracle to flat views * fix(compute): restore onehot_chunk_size on RootPolyMeta Downstream root-poly adapters key one-hot commit planning on the chunk size; the reconciliation dropped it with the tensor cleanup. * fix(compute): restore one-hot chunk-size metadata on root polys OneHotPoly and MultilinearPolynomial expose their K through RootPolyMeta again; downstream commit packaging derives the group's one-hot flavor from it. * fix(prover): close optimization review gaps Route root release and grouped one-hot commitments through their inner implementations. Share quotient planning and one-hot storage dispatch, and split ring-switch execution from the CPU backend. Allow expected profile cache releases, remove the orphan schedule, and add Q32, concurrency, and path-specific regression coverage. * fix(onehot): make block cache caller-owned Keep persistent block storage under explicit caller control. Ordinary commits stream from canonical indices, while opening operations borrow prepared blocks or use operation-local materialization. Remove the protocol-wide release hook so proving cannot evict state shared by downstream clones. * fix(cache): make tensor and NTT retention explicit Keep tensor-root projections operation-local unless callers prepare them explicitly. Retain shared NTT state by default and expose an open root-fold lifecycle hook plus an opt-in release wrapper. Propagate release failures and deduplicate aliased cache owners. * refactor(profile): split workload module Move proof sizing, profile data, claims, cache checks, and each execution scenario into focused modules. Preserve the existing workload facade and runtime behavior while keeping every Rust file below the CI line cap. * docs(spec): define PR 375 end state * fix(cache): align NTT prewarm and release Remove released cache keys so smaller requests rebuild their exact extent. Route prewarming and planned memory through the backend retention policy used by CPU ring-switch streaming. Document lifecycle and concurrency rules, and complete local unsafe safety arguments. * refactor(onehot): unify sparse block representation * refactor(commit): finish source-typed kernel cutover * perf(onehot): unify commit sweep scheduling Use one group driver for range materialization, worker partitioning, scratch-bounded tiling, and source-ordered output. Retain only the measured bucketed and merge production sweeps. Keep direct arithmetic as a test oracle, expose route statistics in tracing, and document the exact selector and 8 MiB per-worker budget. * fix(commit): restore group source parallelism Run independent dense and sparse-ring sources through the existing feature-gated parallel iterator inside their group kernels. One-hot groups keep their fused scheduler. Addresses review discussion #3742146391. * refactor(onehot): remove mutable derived caches * ci(profile): improve reports and narrow builds Describe the exact public opening statements and split the compact report into readable timing, memory, and proof size tables. Build each matrix group with its narrow feature while preserving the profile-ci fallback for older merge bases. Fold the current coverage contract into the Book and archive the stale coverage spec. Adapts the useful changes from draft PRs #360, #361, and #362 to the current benchmark matrix. * refactor(ring-switch): unify quotient planning Remove the temporary fixed ring-switch backend trait and row-plan wrappers so source-typed kernels own the CPU implementation directly. Make cached and streamed quotient paths share validated matrix geometry, bounds, and CRT chunk planning. Strengthen exact-prefix and wide-accumulation boundary tests, and remove unused public wide-ring helpers. * ci(profile): unify fold report layout Join planner choices, witness geometry, and proof costs in one row per fold. Emit group-specific records for multi-group roots and setup offloads so distinct commitment parameters remain visible. Keep percentage deltas for measured costs and exact merge-base values for discrete planner choices. * chore(pr375): complete validation slice Repair the explicit D64 root commitment benchmark without weakening production schedule checks. Record one hot route decisions in profile reports and validate the parser against the isolated CI artifact.\n\nReplace the optimization book stub, align backend and ownership docs, and close the PR 375 spec with benchmark, compatibility, and lifecycle evidence. * fix(prover): close independent review gaps Preserve exact NTT operation requirements through backend routing, join retained cache requests by physical owner, and batch homogeneous projection groups through their child kernels. Remove leaked commitment-only bounds and the streamed source wrapper, split oversized modules, add routing regressions, and archive the implemented PR specification. * fix(profile): correct multi-group report values * feat(profile): compare verifier thread modes * refactor(profile): clarify fold report cells Group planner, work, and proof details into labeled blocks while preserving the side-by-side fold comparison. Keep multi-group precommitment, final-group, and setup-offload parameters visibly separate. * fix(profile): remove persisted bench setup Construct recursive benchmark setup in memory for every sample instead of enabling disk persistence. This prevents public matrix and prefix registry caches from entering per-group handoff artifacts and makes setup timing measure fresh construction. * docs(pr375): reopen CPU policy spec Define configurable CPU resource limits on the existing backend, complete NTT release semantics, and the downstream Jolt validation slices. * feat(compute): configure CPU resource limits * fix(compute): release all CPU NTT caches * fix(compute): retain compression NTT cache * feat(profile): report CPU resource limits * fix(ntt): fail closed on unsafe streaming Reject ring-switch work that exceeds the streamed CRT capacity before entering the retained-cache path. This preserves the configured memory bound and adds a regression proving no cache slot is built on failure. * fix(profile): close benchmark report review gaps * fix(prover): resolve routed cache review findings * fix(ci): align recursive profile linkage * fix(ci): align recursive profile linkage * fix(ci): restore balanced nextest shards * fix(profile): address benchmark review follow-ups Resolve schedule features transitively across crate boundaries and derive profile linkage allowlists from that canonical graph.\n\nThread the exact final group layout into benchmark reporting, reject incomplete public group metadata, and simplify compact report tables.\n\nAlso make workflow-step test failures explicit. --------- Co-authored-by: Markos Georghiades <mgeorghiades@a16z.com> Co-authored-by: sumchecker <241190306+sumchecker@users.noreply.github.com>
* perf(onehot): keep big blocks on the tiled column sweep; row-pass accumulators
- Oversized blocks (hot count beyond the wide accumulators' 2^15 headroom)
are split into cap-respecting sub-blocks and swept through the bucketed
tiled kernel, then re-merged per parent — the previous per-block safe
fallback re-streamed n_a A rings per hot coefficient (~15 TB at a 2^26
one-hot trace column group) and dominated trace-scale commits. Measured:
2^26 K=256 jolt prove 333s -> 154s (commit ~252s -> ~80s).
- Row-pass accumulation (one wide ring per block live at a time) with the
tile budget parameterized; sweep-structure and shift-accumulate
microbenches added (ignored) for A/B iteration.
- In-place NEON AddAssign/SubAssign for Fp128x8i32 (no temp round-trip;
measured neutral on M4 — kept for clarity of intent).
- inner_ajtai_wide_onehot_safe retained as the overflow-safety reference.
* perf(onehot): fused multi-polynomial commit sweep
Every polynomial of a committed group multiplies the same A matrix, but
the sweep ran per-poly, re-streaming A once per polynomial — the
dominant commit traffic at trace scale (~11 TB at 2^26/K256, ~28 s of
the ~82 s commit).
Adds a merge-based fused kernel (column_sweep_core_merge): blocks carry
cursors over their position-sorted entries and the kernel walks A
columns in L1-sized chunks, so one A pass serves every block of every
polynomial in the batch — and the counting/scatter pass (whose packed
buffer scales with tile size) disappears. Wiring:
CommitmentComputeBackend::onehot_commit_rows_multi (default loops the
single-poly path), RootCommitKernel::commit_inner_group (default
per-source), OneHotPoly::commit_inner_group, and the batched-prove
commit path groups the whole batch.
Byte-equal to per-poly sweeps (new equality test covers core-vs-core,
wrapper round-trip, and tile-size independence); 223 unit tests pass.
* perf(onehot): keep merge-sweep accumulator tile and column chunk L1-resident
The fused sweep ran its 58-block accumulator set (116 KB) plus a 64 KB
widened-column chunk out of L2, costing ~1.5x per accumulate (68 vs
28 ns in the tight-tile bench). Cap the merge tile at 32 blocks of
accumulators (64 KB) and halve the column chunk to 16 (32 KB) so both
live in L1; the extra A re-streaming this causes is negligible after
poly fusion. Also spans the fold grind (fold_grind_sample) for opening
attribution.
* fix(onehot): self-reduce merge-sweep accumulators at the cap; no block splitting
At trace scale every one-hot block exceeds the 2^15 wide-accumulator
cap (~2^19 accumulations at ppb 2^21), so the merge path inherited the
splitting wrapper's 16x sub-block explosion — which turned the L1 tile
cap into a 29x A re-stream and mostly-empty cursor scans (2^26 prove
regressed to 252s). Fold each block's wide accumulator into a canonical
partial whenever it reaches the cap instead: a handful of reduces per
block-row, no splitting, blocks keep dense full-range chunks, and small
L1 tiles are genuinely safe. New regression pins the self-reducing
kernel against the splitting wrapper on an oversized block.
* perf(onehot): bench-tuned merge-sweep defaults (tile 64 blocks, chunk 32 cols)
Full (tile x chunk) matrix at a trace-like sparse shape is flat within
5-30%; (64, 32) is the minimum at 61.4 ns/accum vs 64.4 at the previous
default. The kernel is at its practical floor for quarter-density
one-hot blocks — the 28 ns dense-shape bench figure does not transfer.
Adds the merge_sweep_bench tuning matrix (ignored).
* feat(onehot): expose clear_block_cache on OneHotPoly
The per-block storage is a pure cache over the retained hot indices
(blocks_for rebuilds it on demand), but it is trace-scale — ~8 bytes per
hot entry across every committed column — and otherwise lives from commit
until the opening fold. Callers that know the sweep is done can drop it
and pay one parallel rebuild inside the fold.
* perf(prover): build the setup NTT envelope slot lazily and let callers drop it between uses — 2^26 jolt e2e peak 87.7->81.3GB, prove 118.8->99.4s
Registration at prepare_setup now only reserves the slot cell and records
the CRT profile; with_shared_ntt builds single-flight on first use, and
drop_built_ntt_slots() returns built slots to the reserved state (next use
rebuilds, ~1.9s at the jolt 2^26 envelope). Keeps the ~28GiB transformed
matrix out of every window that doesn't touch it: unused setups never
build, and the caller can drop between the commit's terminal product and
the stage-8 fold.
* perf(prover): extent-sized NTT slots + streamed root-relation transforms — 2^26 jolt e2e peak 87.7->55.1GB, prove 118.8->95.6s
Three changes that together keep the transformed matrix out of the fold
window (it was ~30GiB of standing memory at the jolt 2^26 shape):
- with_shared_ntt(extent, f): consumers declare the rows x width extent
they slice; slots build at the rounded request (smallest built cover
reused, envelope-warmed setups unchanged). The commit's terminal
product now builds 5GiB instead of 30.
- ring-switch relation/quotient rows above 2^21 rings stream per-element
transforms from A's field form inside the tile loop (new cyc-only ring
constructor; chunked z-quotient mirrors the cached path's bracketing;
both paths bit-identical to the cache fill — equality tests included).
The root relation reads each element once per prove, so caching its
transform was pure standing memory.
- fold-entry envelope warms (prove.rs role/terminal, ring-switch
commit_w/commit_terminal_w) skip the base ring dim: its consumers
self-serve extent-sized, cross-D dims keep the eager warm.
AKITA_NTT_BUILD_BACKTRACE=1 dumps a backtrace per slot build for future
residency diagnosis.
* perf(prover): drop built NTT slots after the root fold level — fold window 51.3->49.3GiB sampled at 2^26, prove unchanged
Root-level slots dwarf every deeper level's; releasing them keeps the
fold tail (where sumcheck transients spike) off root-scale caches, and
deeper levels rebuild their own sub-GiB slots on first use. Peak-neutral
on its own (the stage-3/4 transient now binds at ~51 GiB sampled) —
rides with the stage-window items.
* perf(prover): releasable setup matrix with seed re-derivation — 2^26 jolt e2e peak 55.1->47.1GB, prove 95.6->99.3s
The field-form matrix is a pure function of the public seed, so its
backing store is cache. AkitaExpandedSetup.shared_matrix becomes
SharedSetupMatrix: seed-backed shape metadata (envelope keys and
validation never shrink), covering_at_dyn/full accessors over a swappable
Arc store, release_to_prefix, and a per-element MatrixElementDeriver
(same XOF stream as derive_public_matrix_flat — bit-identical values,
equality-tested). Derivation is captured as monomorphized fn pointers at
construction, so consumers need no extra bounds.
After the commit sweep (its last full-width reader) the store releases
to a 2^21-ring prefix that serves slot rebuilds and small setup reads;
wide readers stream per element: the ring-switch relation kernels take a
StreamedASource (materialized pre-release, seed-derived after), and the
relation weight events read per-column ring slices through
SetupRowFamily instead of full row views. Setup-contribution scans cover
only the rows they read, not padded view lengths. A full-width covering
re-caches the store (the verifier's root-level scan legitimately reads
the whole matrix; later proves re-release), so within one process the
~2.4s derive amortizes to once per prove/verify cycle — first-verify
cost 0.2->2.6s, standalone verifiers already pay full derivation at
setup.
* perf(prover): build commit-sweep block tiles lazily from index columns — 2^26 jolt e2e peak 47.1->44.1GB, commit window 43.7->35.1GiB
The fused group commit materialized every polynomial's block entries
(~200 B/cycle across a 29-poly batch) before sweeping. Blocks partition
the ring-element index space contiguously, so from_indices gains
block-range variants reading exactly a tile's slice of the retained
index columns; per-tile builds across the sweep sum to the same single
pass. commit_inner_group hands the sweep LazyOneHotBlocks builders
(OneHotCommitBlocks::{SingleChunk,MultiChunk}Lazy) and each thread
materializes one L2 tile at a time (~2 GiB in flight for the batch),
dropping it after the pass. The tile pass is extracted from the merge
kernel and shared, so lazy results are byte-equal to eager
(equality-tested). Side effect: later windows drop ~6-9 GiB too — the
cache's freed pages no longer linger in the allocator. Single-poly and
small-batch paths keep the eager cache.
* perf(prover): stream fold blocks from index columns — per-block builds in fold_blocks/_ring, per-(block,position-window) builds in the tensor accumulate
The fold's opening-claim evaluation was the sole builder of the full
per-block entry cache (probe-verified: every blocks_for build flowed
through fold_kernels::evaluate_claims_at_prepared_point), and the tensor
decompose reused it. Both consumers visit each entry range exactly once:
fold_blocks/_ring build one block at a time, and
onehot_accumulate_tensor_lazy builds one (block, position-window) at a
time (LazyOneHotBlocks generalized to ring-range builds; pos_in_block
stays absolute). Total build work equals one pass over the retained
index columns; the eager tensor kernel stays as the reference for the
lazy-equality test. 2^26 measurement pending a quiet machine (first
attempt voided by mid-run user activity).
* refactor: shrink the perf-branch diff before upstreaming
- delete measured-dead code: Fp128Lazy (negative A/B result), the
row-outer sweep variant + bench entry, inner_ajtai_wide_onehot_safe
- delete perf-campaign scaffolding: planner candidate dump, three
env-gated backtrace blocks
- from_indices delegates to the ring-range builders; one view_layout
helper replaces triplicated validation in OneHotPoly; lazy fold arms
collapse into a macro; base-dim NTT warm skip moves into
ensure_envelope_ntt (one home instead of five call sites)
- fix two real residency leaks the review found: the tensor decompose
layout probe rebuilt+cached a full block set inside the fold, and two
setup-prefix readers materialized the full matrix post-release
- export NTT_STREAM_THRESHOLD_RING_ELEMENTS + a pinned
release_setup_matrix_to_streaming_prefix so the retained prefix and
the streaming threshold cannot drift apart
- fix two doc comments stranded on the wrong item by earlier insertions
2^26 validation: prove 101.3s / peak 44.09GB (unchanged).
* perf(algebra): fuse fixed-size wide ring shifts
* perf(algebra): expose wide ring coefficients
* perf(prover): consume decompose witness rows without copies
* perf(prover): release root opening storage after fold
* fix(profile): propagate commit accumulator bounds
* perf(prover): cache only required commit transform
* perf(prover): stream CRT-chunked quotient roles
* perf(prover): parallelize relation setup columns
* perf(prover): coalesce aligned relation events
* perf(prover): parallelize exact-prefix folds
* fix(prover): preserve group-local relation dimensions
* perf(planner): trade payload slack for narrower roots
* fix(planner): preserve scalar rank policy through recursion
* fix(planner): emit rustfmt-stable catalog imports
* fix(planner): remove stale lint expectation
* fix(types): size released setup without rebuilding
* test(prover): drop local tuning probes
* fix(pcs): update releasable setup call sites
* test(pcs): align mixed-D cache test with lazy setup
* test(prover): split oversized backend test modules
* feat(planner): decouple inner commitment basis
Search inner and opening decomposition bases independently across dense, setup-prefix, and recursive commitments.
Add exact i16 commitment support, wider SIS buckets, padded-prefix-aware selection, and dense nv26 schedule coverage.
* fix(planner): bound independent-basis search
Keep distinct next-witness classes so recursive feasibility remains exact, while dropping only dominated one-digit inner bases.
Bound suffix memoization and catalog workers to prevent schedule regeneration from exhausting memory after the search-domain expansion.
* refactor(prover): split CPU backend by role
* perf(planner): evict bounded memo entries
* fix(planner): skip discarded sweep work
* test(config): expect independent dense basis
* fix(planner): decouple precommit inner basis
* refactor(planner): tighten candidate context
* feat(planner): preserve partial regen rows
* chore(schedules): restore D64 onehot rows
* refactor(prover): isolate CPU prepared setup
* ci(planner): avoid redundant table regeneration
* fix(planner): preserve unsupported regen rows
* feat(planner)!: complete independent basis search
Decouple raw inner commitment bases from opening bases while preserving exact one-hot and recursive balanced-digit sources.
Add padded-prefix Pareto planning, profile-native precommits, canonical signed-digit support, bounded exact suffix search, atomic catalog publishing, and full SIS policy audit artifacts.
Preserve the established complete-schedule selection objectives while refining recursive setup scoring to physical padded capacity.
* fix(ci): refresh precommit registries
Remove the stale all-schedules test import and regenerate standalone precommit profiles with the merged independent-basis planner so catalog coverage matches runtime lookup.
* fix(sis): harden table publication
Require the canonical proven-pruned profile and mandatory validation for production Rust artifacts. Correct certificate provenance and table digests, align the parallel exhaustive diagnostic profile with the quantum LGSA model, and split oversized estimator/type test modules.
* fix(schedules): refresh SIS table identity
Propagate the corrected SIS provenance digest into all generated catalog identities and precommitted descriptors. Recompute the four affected catalog key digests so validation remains fail closed without changing schedules or numeric SIS rows.
* chore(sis): remove obsolete audit CSV
Keep generated_sis_table/policy_audit.csv as the only canonical production SIS certificate. Direct full comparison jobs to temporary output and align the book and estimator documentation with the proven-pruned shared-digest policy.
* revert(planner): drop payload-slack schedule selection
Removes the rank-aware selection policy (payload slack for narrower
roots) per review feedback: the planner is being reworked upstream and
this policy should not bake into catalog identity. Restores
akita-planner, akita-config, and akita-schedules to their origin/main
state; the prover optimizations in this branch are unaffected.
* fix(ci): repair doc links and glue trait bound
Rustdoc denies public docs linking private items; the trusted-artifact
glue impl needs RandomSampling for seed re-derived setup construction.
* fix(tests): drop tensor-kernel test and port oracle to flat views
* fix(compute): restore onehot_chunk_size on RootPolyMeta
Downstream root-poly adapters key one-hot commit planning on the
chunk size; the reconciliation dropped it with the tensor cleanup.
* fix(compute): restore one-hot chunk-size metadata on root polys
OneHotPoly and MultilinearPolynomial expose their K through
RootPolyMeta again; downstream commit packaging derives the group's
one-hot flavor from it.
* fix(prover): close optimization review gaps
Route root release and grouped one-hot commitments through their inner implementations. Share quotient planning and one-hot storage dispatch, and split ring-switch execution from the CPU backend.
Allow expected profile cache releases, remove the orphan schedule, and add Q32, concurrency, and path-specific regression coverage.
* fix(onehot): make block cache caller-owned
Keep persistent block storage under explicit caller control. Ordinary commits stream from canonical indices, while opening operations borrow prepared blocks or use operation-local materialization. Remove the protocol-wide release hook so proving cannot evict state shared by downstream clones.
* fix(cache): make tensor and NTT retention explicit
Keep tensor-root projections operation-local unless callers prepare them explicitly. Retain shared NTT state by default and expose an open root-fold lifecycle hook plus an opt-in release wrapper. Propagate release failures and deduplicate aliased cache owners.
* refactor(profile): split workload module
Move proof sizing, profile data, claims, cache checks, and each execution scenario into focused modules. Preserve the existing workload facade and runtime behavior while keeping every Rust file below the CI line cap.
* docs(spec): define PR 375 end state
* fix(cache): align NTT prewarm and release
Remove released cache keys so smaller requests rebuild their exact extent. Route prewarming and planned memory through the backend retention policy used by CPU ring-switch streaming. Document lifecycle and concurrency rules, and complete local unsafe safety arguments.
* refactor(onehot): unify sparse block representation
* refactor(commit): finish source-typed kernel cutover
* perf(onehot): unify commit sweep scheduling
Use one group driver for range materialization, worker partitioning, scratch-bounded tiling, and source-ordered output.
Retain only the measured bucketed and merge production sweeps. Keep direct arithmetic as a test oracle, expose route statistics in tracing, and document the exact selector and 8 MiB per-worker budget.
* fix(commit): restore group source parallelism
Run independent dense and sparse-ring sources through the existing feature-gated parallel iterator inside their group kernels. One-hot groups keep their fused scheduler.
Addresses review discussion #3742146391.
* refactor(onehot): remove mutable derived caches
* ci(profile): improve reports and narrow builds
Describe the exact public opening statements and split the compact report into readable timing, memory, and proof size tables.
Build each matrix group with its narrow feature while preserving the profile-ci fallback for older merge bases. Fold the current coverage contract into the Book and archive the stale coverage spec.
Adapts the useful changes from draft PRs #360, #361, and #362 to the current benchmark matrix.
* perf(planner): reduce recursive sizing overhead
Add exact scalar witness sizing, reuse bounded checked compression plans, and reject non-contracting recursive split bodies before B/D construction. Preserve the exhaustive candidate domain and verify against the unpruned planner oracle.\n\nKnown limitation: fp128_onehot_recursive still does not finish catalog generation within the measured five-minute window, so this checkpoint does not resolve the stale recursive catalog blocker.
* refactor(ring-switch): unify quotient planning
Remove the temporary fixed ring-switch backend trait and row-plan wrappers so source-typed kernels own the CPU implementation directly.
Make cached and streamed quotient paths share validated matrix geometry, bounds, and CRT chunk planning. Strengthen exact-prefix and wide-accumulation boundary tests, and remove unused public wide-ring helpers.
* ci(profile): unify fold report layout
Join planner choices, witness geometry, and proof costs in one row per fold. Emit group-specific records for multi-group roots and setup offloads so distinct commitment parameters remain visible.
Keep percentage deltas for measured costs and exact merge-base values for discrete planner choices.
* chore(pr375): complete validation slice
Repair the explicit D64 root commitment benchmark without weakening production schedule checks. Record one hot route decisions in profile reports and validate the parser against the isolated CI artifact.\n\nReplace the optimization book stub, align backend and ownership docs, and close the PR 375 spec with benchmark, compatibility, and lifecycle evidence.
* fix(prover): close independent review gaps
Preserve exact NTT operation requirements through backend routing, join retained cache requests by physical owner, and batch homogeneous projection groups through their child kernels.
Remove leaked commitment-only bounds and the streamed source wrapper, split oversized modules, add routing regressions, and archive the implemented PR specification.
* fix(profile): correct multi-group report values
* feat(profile): compare verifier thread modes
* perf(planner): retain hot suffix cache entries
Use bounded second-chance eviction within the existing direct and prefixed suffix quotas. A 700,000-insertion census reduced recomputations from about 218,000 to 145,179 without changing candidate coverage.\n\nShare setup and payload frontier keys only in states that retain both projections, and use binary search over monotone SIS rank rows. The exact planner and unpruned oracle suite passes all 21 tests.
* refactor(profile): clarify fold report cells
Group planner, work, and proof details into labeled blocks while preserving the side-by-side fold comparison. Keep multi-group precommitment, final-group, and setup-offload parameters visibly separate.
* perf(planner): bound recursive split frontier
Keep recursive split enumeration exact through twelve reduced variables. For larger multi-candidate suffix states, retain both extremes and a five-point window around the analytic balance point. Ordinary single-winner direct search still checks the complete split domain.
Move prefix-only rejection before child recursion, avoid retaining unused prefix frontiers, and compact present setup lengths with NonZeroUsize. Regenerate both recursive catalog families.
This intentionally trades global optimality for large frontier states for a practical, explicit search policy. Full 14-family generation now completes in 36 seconds.
* fix(sis): restore generated table identity
Reconnect the split artifact digest test and make the generated digest constants the single runtime source of truth. Refresh the q128 Inner/512 extension digest for the checked-in rows and regenerate every catalog identity.
Also replace the compression planner's manual loop counter so all configured CI Clippy graphs pass.
* fix(profile): remove persisted bench setup
Construct recursive benchmark setup in memory for every sample instead of enabling disk persistence. This prevents public matrix and prefix registry caches from entering per-group handoff artifacts and makes setup timing measure fresh construction.
* fix(prover): preserve i16 opening digits
* docs(pr375): reopen CPU policy spec
Define configurable CPU resource limits on the existing backend, complete NTT release semantics, and the downstream Jolt validation slices.
* refactor(planner): resolve review findings
* feat(compute): configure CPU resource limits
* fix(planner): preserve terminal basis winners
* fix(compute): release all CPU NTT caches
* fix(compute): retain compression NTT cache
* feat(profile): report CPU resource limits
* perf(ntt): use exact tail for quotients
Route centered relation quotients through the existing five-prime CRT prefix plus the 12289 exactness tail when one base term cannot fit. Prewarm only the affected A prefixes and keep scalar arithmetic as the final capacity fallback.
Parallelize independent exact-i16 commitment blocks and restore the advertised setup-first objective for direct grouped schedule generation.
* test(ntt): guard small-field cache plans
Lock the fp32 and fp64 dense nv26 schedules to their ordinary cached NTT domains and verify that catalog planning agrees with field-typed runtime tail selection across every modulus profile.
Remove the stale profile label that described shipped D128 catalogs as runtime-DP schedules.
* ci(profile): clarify dense benchmark suite
Rename the existing three-field dense benchmark leg instead of duplicating runner work.
Show the resolved A/B/D fold schedule in compact reports and update the active profiling documentation to explain why fp128 uses D256 only for the root A role.
* fix(ntt): fail closed on unsafe streaming
Reject ring-switch work that exceeds the streamed CRT capacity before entering the retained-cache path. This preserves the configured memory bound and adds a regression proving no cache slot is built on failure.
* test(pcs): dispatch adaptive opening dimensions
* fix(pcs): generalize adaptive opening helper
* fix(profile): close benchmark report review gaps
* refactor(planner): centralize admission and pruning
Route precommit admission, scalar sizing, Pareto insertion, recursive split traversal, and generated-family providers through their canonical owners. Harden descriptor validation, entropy coverage, SIS limits, output isolation, and exact i16 composition tests in response to review.
* fix(prover): resolve routed cache review findings
* fix(ci): align recursive profile linkage
* fix(ci): align recursive profile linkage
* fix(ci): restore balanced nextest shards
* test(pcs): pin adaptive W8R2 schedule shape
* fix(profile): address benchmark review follow-ups
Resolve schedule features transitively across crate boundaries and derive profile linkage allowlists from that canonical graph.\n\nThread the exact final group layout into benchmark reporting, reject incomplete public group metadata, and simplify compact report tables.\n\nAlso make workflow-step test failures explicit.
* fix(ci): isolate recursion build cache
Key the recursion smoke target cache by the exact source revision. This prevents path dependencies outside the recursion workspace from restoring stale artifacts across PRs.
---------
Co-authored-by: Markos Georghiades <mgeorghiades@a16z.com>
Co-authored-by: sumchecker <241190306+sumchecker@users.noreply.github.com>
* perf(onehot): keep big blocks on the tiled column sweep; row-pass accumulators
- Oversized blocks (hot count beyond the wide accumulators' 2^15 headroom)
are split into cap-respecting sub-blocks and swept through the bucketed
tiled kernel, then re-merged per parent — the previous per-block safe
fallback re-streamed n_a A rings per hot coefficient (~15 TB at a 2^26
one-hot trace column group) and dominated trace-scale commits. Measured:
2^26 K=256 jolt prove 333s -> 154s (commit ~252s -> ~80s).
- Row-pass accumulation (one wide ring per block live at a time) with the
tile budget parameterized; sweep-structure and shift-accumulate
microbenches added (ignored) for A/B iteration.
- In-place NEON AddAssign/SubAssign for Fp128x8i32 (no temp round-trip;
measured neutral on M4 — kept for clarity of intent).
- inner_ajtai_wide_onehot_safe retained as the overflow-safety reference.
* perf(onehot): fused multi-polynomial commit sweep
Every polynomial of a committed group multiplies the same A matrix, but
the sweep ran per-poly, re-streaming A once per polynomial — the
dominant commit traffic at trace scale (~11 TB at 2^26/K256, ~28 s of
the ~82 s commit).
Adds a merge-based fused kernel (column_sweep_core_merge): blocks carry
cursors over their position-sorted entries and the kernel walks A
columns in L1-sized chunks, so one A pass serves every block of every
polynomial in the batch — and the counting/scatter pass (whose packed
buffer scales with tile size) disappears. Wiring:
CommitmentComputeBackend::onehot_commit_rows_multi (default loops the
single-poly path), RootCommitKernel::commit_inner_group (default
per-source), OneHotPoly::commit_inner_group, and the batched-prove
commit path groups the whole batch.
Byte-equal to per-poly sweeps (new equality test covers core-vs-core,
wrapper round-trip, and tile-size independence); 223 unit tests pass.
* perf(onehot): keep merge-sweep accumulator tile and column chunk L1-resident
The fused sweep ran its 58-block accumulator set (116 KB) plus a 64 KB
widened-column chunk out of L2, costing ~1.5x per accumulate (68 vs
28 ns in the tight-tile bench). Cap the merge tile at 32 blocks of
accumulators (64 KB) and halve the column chunk to 16 (32 KB) so both
live in L1; the extra A re-streaming this causes is negligible after
poly fusion. Also spans the fold grind (fold_grind_sample) for opening
attribution.
* fix(onehot): self-reduce merge-sweep accumulators at the cap; no block splitting
At trace scale every one-hot block exceeds the 2^15 wide-accumulator
cap (~2^19 accumulations at ppb 2^21), so the merge path inherited the
splitting wrapper's 16x sub-block explosion — which turned the L1 tile
cap into a 29x A re-stream and mostly-empty cursor scans (2^26 prove
regressed to 252s). Fold each block's wide accumulator into a canonical
partial whenever it reaches the cap instead: a handful of reduces per
block-row, no splitting, blocks keep dense full-range chunks, and small
L1 tiles are genuinely safe. New regression pins the self-reducing
kernel against the splitting wrapper on an oversized block.
* perf(onehot): bench-tuned merge-sweep defaults (tile 64 blocks, chunk 32 cols)
Full (tile x chunk) matrix at a trace-like sparse shape is flat within
5-30%; (64, 32) is the minimum at 61.4 ns/accum vs 64.4 at the previous
default. The kernel is at its practical floor for quarter-density
one-hot blocks — the 28 ns dense-shape bench figure does not transfer.
Adds the merge_sweep_bench tuning matrix (ignored).
* feat(onehot): expose clear_block_cache on OneHotPoly
The per-block storage is a pure cache over the retained hot indices
(blocks_for rebuilds it on demand), but it is trace-scale — ~8 bytes per
hot entry across every committed column — and otherwise lives from commit
until the opening fold. Callers that know the sweep is done can drop it
and pay one parallel rebuild inside the fold.
* perf(prover): build the setup NTT envelope slot lazily and let callers drop it between uses — 2^26 jolt e2e peak 87.7->81.3GB, prove 118.8->99.4s
Registration at prepare_setup now only reserves the slot cell and records
the CRT profile; with_shared_ntt builds single-flight on first use, and
drop_built_ntt_slots() returns built slots to the reserved state (next use
rebuilds, ~1.9s at the jolt 2^26 envelope). Keeps the ~28GiB transformed
matrix out of every window that doesn't touch it: unused setups never
build, and the caller can drop between the commit's terminal product and
the stage-8 fold.
* perf(prover): extent-sized NTT slots + streamed root-relation transforms — 2^26 jolt e2e peak 87.7->55.1GB, prove 118.8->95.6s
Three changes that together keep the transformed matrix out of the fold
window (it was ~30GiB of standing memory at the jolt 2^26 shape):
- with_shared_ntt(extent, f): consumers declare the rows x width extent
they slice; slots build at the rounded request (smallest built cover
reused, envelope-warmed setups unchanged). The commit's terminal
product now builds 5GiB instead of 30.
- ring-switch relation/quotient rows above 2^21 rings stream per-element
transforms from A's field form inside the tile loop (new cyc-only ring
constructor; chunked z-quotient mirrors the cached path's bracketing;
both paths bit-identical to the cache fill — equality tests included).
The root relation reads each element once per prove, so caching its
transform was pure standing memory.
- fold-entry envelope warms (prove.rs role/terminal, ring-switch
commit_w/commit_terminal_w) skip the base ring dim: its consumers
self-serve extent-sized, cross-D dims keep the eager warm.
AKITA_NTT_BUILD_BACKTRACE=1 dumps a backtrace per slot build for future
residency diagnosis.
* perf(prover): drop built NTT slots after the root fold level — fold window 51.3->49.3GiB sampled at 2^26, prove unchanged
Root-level slots dwarf every deeper level's; releasing them keeps the
fold tail (where sumcheck transients spike) off root-scale caches, and
deeper levels rebuild their own sub-GiB slots on first use. Peak-neutral
on its own (the stage-3/4 transient now binds at ~51 GiB sampled) —
rides with the stage-window items.
* perf(prover): releasable setup matrix with seed re-derivation — 2^26 jolt e2e peak 55.1->47.1GB, prove 95.6->99.3s
The field-form matrix is a pure function of the public seed, so its
backing store is cache. AkitaExpandedSetup.shared_matrix becomes
SharedSetupMatrix: seed-backed shape metadata (envelope keys and
validation never shrink), covering_at_dyn/full accessors over a swappable
Arc store, release_to_prefix, and a per-element MatrixElementDeriver
(same XOF stream as derive_public_matrix_flat — bit-identical values,
equality-tested). Derivation is captured as monomorphized fn pointers at
construction, so consumers need no extra bounds.
After the commit sweep (its last full-width reader) the store releases
to a 2^21-ring prefix that serves slot rebuilds and small setup reads;
wide readers stream per element: the ring-switch relation kernels take a
StreamedASource (materialized pre-release, seed-derived after), and the
relation weight events read per-column ring slices through
SetupRowFamily instead of full row views. Setup-contribution scans cover
only the rows they read, not padded view lengths. A full-width covering
re-caches the store (the verifier's root-level scan legitimately reads
the whole matrix; later proves re-release), so within one process the
~2.4s derive amortizes to once per prove/verify cycle — first-verify
cost 0.2->2.6s, standalone verifiers already pay full derivation at
setup.
* perf(prover): build commit-sweep block tiles lazily from index columns — 2^26 jolt e2e peak 47.1->44.1GB, commit window 43.7->35.1GiB
The fused group commit materialized every polynomial's block entries
(~200 B/cycle across a 29-poly batch) before sweeping. Blocks partition
the ring-element index space contiguously, so from_indices gains
block-range variants reading exactly a tile's slice of the retained
index columns; per-tile builds across the sweep sum to the same single
pass. commit_inner_group hands the sweep LazyOneHotBlocks builders
(OneHotCommitBlocks::{SingleChunk,MultiChunk}Lazy) and each thread
materializes one L2 tile at a time (~2 GiB in flight for the batch),
dropping it after the pass. The tile pass is extracted from the merge
kernel and shared, so lazy results are byte-equal to eager
(equality-tested). Side effect: later windows drop ~6-9 GiB too — the
cache's freed pages no longer linger in the allocator. Single-poly and
small-batch paths keep the eager cache.
* perf(prover): stream fold blocks from index columns — per-block builds in fold_blocks/_ring, per-(block,position-window) builds in the tensor accumulate
The fold's opening-claim evaluation was the sole builder of the full
per-block entry cache (probe-verified: every blocks_for build flowed
through fold_kernels::evaluate_claims_at_prepared_point), and the tensor
decompose reused it. Both consumers visit each entry range exactly once:
fold_blocks/_ring build one block at a time, and
onehot_accumulate_tensor_lazy builds one (block, position-window) at a
time (LazyOneHotBlocks generalized to ring-range builds; pos_in_block
stays absolute). Total build work equals one pass over the retained
index columns; the eager tensor kernel stays as the reference for the
lazy-equality test. 2^26 measurement pending a quiet machine (first
attempt voided by mid-run user activity).
* refactor: shrink the perf-branch diff before upstreaming
- delete measured-dead code: Fp128Lazy (negative A/B result), the
row-outer sweep variant + bench entry, inner_ajtai_wide_onehot_safe
- delete perf-campaign scaffolding: planner candidate dump, three
env-gated backtrace blocks
- from_indices delegates to the ring-range builders; one view_layout
helper replaces triplicated validation in OneHotPoly; lazy fold arms
collapse into a macro; base-dim NTT warm skip moves into
ensure_envelope_ntt (one home instead of five call sites)
- fix two real residency leaks the review found: the tensor decompose
layout probe rebuilt+cached a full block set inside the fold, and two
setup-prefix readers materialized the full matrix post-release
- export NTT_STREAM_THRESHOLD_RING_ELEMENTS + a pinned
release_setup_matrix_to_streaming_prefix so the retained prefix and
the streaming threshold cannot drift apart
- fix two doc comments stranded on the wrong item by earlier insertions
2^26 validation: prove 101.3s / peak 44.09GB (unchanged).
* perf(algebra): fuse fixed-size wide ring shifts
* perf(algebra): expose wide ring coefficients
* perf(prover): consume decompose witness rows without copies
* perf(prover): release root opening storage after fold
* fix(profile): propagate commit accumulator bounds
* perf(prover): cache only required commit transform
* perf(prover): stream CRT-chunked quotient roles
* perf(prover): parallelize relation setup columns
* perf(prover): coalesce aligned relation events
* perf(prover): parallelize exact-prefix folds
* fix(prover): preserve group-local relation dimensions
* perf(planner): trade payload slack for narrower roots
* fix(planner): preserve scalar rank policy through recursion
* fix(planner): emit rustfmt-stable catalog imports
* fix(planner): remove stale lint expectation
* fix(types): size released setup without rebuilding
* test(prover): drop local tuning probes
* fix(pcs): update releasable setup call sites
* test(pcs): align mixed-D cache test with lazy setup
* test(prover): split oversized backend test modules
* feat(planner): decouple inner commitment basis
Search inner and opening decomposition bases independently across dense, setup-prefix, and recursive commitments.
Add exact i16 commitment support, wider SIS buckets, padded-prefix-aware selection, and dense nv26 schedule coverage.
* fix(planner): bound independent-basis search
Keep distinct next-witness classes so recursive feasibility remains exact, while dropping only dominated one-digit inner bases.
Bound suffix memoization and catalog workers to prevent schedule regeneration from exhausting memory after the search-domain expansion.
* refactor(prover): split CPU backend by role
* perf(planner): evict bounded memo entries
* fix(planner): skip discarded sweep work
* test(config): expect independent dense basis
* fix(planner): decouple precommit inner basis
* refactor(planner): tighten candidate context
* feat(planner): preserve partial regen rows
* chore(schedules): restore D64 onehot rows
* refactor(prover): isolate CPU prepared setup
* ci(planner): avoid redundant table regeneration
* fix(planner): preserve unsupported regen rows
* feat(planner)!: complete independent basis search
Decouple raw inner commitment bases from opening bases while preserving exact one-hot and recursive balanced-digit sources.
Add padded-prefix Pareto planning, profile-native precommits, canonical signed-digit support, bounded exact suffix search, atomic catalog publishing, and full SIS policy audit artifacts.
Preserve the established complete-schedule selection objectives while refining recursive setup scoring to physical padded capacity.
* fix(ci): refresh precommit registries
Remove the stale all-schedules test import and regenerate standalone precommit profiles with the merged independent-basis planner so catalog coverage matches runtime lookup.
* fix(sis): harden table publication
Require the canonical proven-pruned profile and mandatory validation for production Rust artifacts. Correct certificate provenance and table digests, align the parallel exhaustive diagnostic profile with the quantum LGSA model, and split oversized estimator/type test modules.
* fix(schedules): refresh SIS table identity
Propagate the corrected SIS provenance digest into all generated catalog identities and precommitted descriptors. Recompute the four affected catalog key digests so validation remains fail closed without changing schedules or numeric SIS rows.
* chore(sis): remove obsolete audit CSV
Keep generated_sis_table/policy_audit.csv as the only canonical production SIS certificate. Direct full comparison jobs to temporary output and align the book and estimator documentation with the proven-pruned shared-digest policy.
* revert(planner): drop payload-slack schedule selection
Removes the rank-aware selection policy (payload slack for narrower
roots) per review feedback: the planner is being reworked upstream and
this policy should not bake into catalog identity. Restores
akita-planner, akita-config, and akita-schedules to their origin/main
state; the prover optimizations in this branch are unaffected.
* fix(ci): repair doc links and glue trait bound
Rustdoc denies public docs linking private items; the trusted-artifact
glue impl needs RandomSampling for seed re-derived setup construction.
* fix(tests): drop tensor-kernel test and port oracle to flat views
* fix(compute): restore onehot_chunk_size on RootPolyMeta
Downstream root-poly adapters key one-hot commit planning on the
chunk size; the reconciliation dropped it with the tensor cleanup.
* fix(compute): restore one-hot chunk-size metadata on root polys
OneHotPoly and MultilinearPolynomial expose their K through
RootPolyMeta again; downstream commit packaging derives the group's
one-hot flavor from it.
* fix(prover): close optimization review gaps
Route root release and grouped one-hot commitments through their inner implementations. Share quotient planning and one-hot storage dispatch, and split ring-switch execution from the CPU backend.
Allow expected profile cache releases, remove the orphan schedule, and add Q32, concurrency, and path-specific regression coverage.
* fix(onehot): make block cache caller-owned
Keep persistent block storage under explicit caller control. Ordinary commits stream from canonical indices, while opening operations borrow prepared blocks or use operation-local materialization. Remove the protocol-wide release hook so proving cannot evict state shared by downstream clones.
* fix(cache): make tensor and NTT retention explicit
Keep tensor-root projections operation-local unless callers prepare them explicitly. Retain shared NTT state by default and expose an open root-fold lifecycle hook plus an opt-in release wrapper. Propagate release failures and deduplicate aliased cache owners.
* refactor(profile): split workload module
Move proof sizing, profile data, claims, cache checks, and each execution scenario into focused modules. Preserve the existing workload facade and runtime behavior while keeping every Rust file below the CI line cap.
* docs(spec): define PR 375 end state
* fix(cache): align NTT prewarm and release
Remove released cache keys so smaller requests rebuild their exact extent. Route prewarming and planned memory through the backend retention policy used by CPU ring-switch streaming. Document lifecycle and concurrency rules, and complete local unsafe safety arguments.
* refactor(onehot): unify sparse block representation
* refactor(commit): finish source-typed kernel cutover
* perf(onehot): unify commit sweep scheduling
Use one group driver for range materialization, worker partitioning, scratch-bounded tiling, and source-ordered output.
Retain only the measured bucketed and merge production sweeps. Keep direct arithmetic as a test oracle, expose route statistics in tracing, and document the exact selector and 8 MiB per-worker budget.
* fix(commit): restore group source parallelism
Run independent dense and sparse-ring sources through the existing feature-gated parallel iterator inside their group kernels. One-hot groups keep their fused scheduler.
Addresses review discussion #3742146391.
* refactor(onehot): remove mutable derived caches
* ci(profile): improve reports and narrow builds
Describe the exact public opening statements and split the compact report into readable timing, memory, and proof size tables.
Build each matrix group with its narrow feature while preserving the profile-ci fallback for older merge bases. Fold the current coverage contract into the Book and archive the stale coverage spec.
Adapts the useful changes from draft PRs #360, #361, and #362 to the current benchmark matrix.
* perf(planner): reduce recursive sizing overhead
Add exact scalar witness sizing, reuse bounded checked compression plans, and reject non-contracting recursive split bodies before B/D construction. Preserve the exhaustive candidate domain and verify against the unpruned planner oracle.\n\nKnown limitation: fp128_onehot_recursive still does not finish catalog generation within the measured five-minute window, so this checkpoint does not resolve the stale recursive catalog blocker.
* refactor(ring-switch): unify quotient planning
Remove the temporary fixed ring-switch backend trait and row-plan wrappers so source-typed kernels own the CPU implementation directly.
Make cached and streamed quotient paths share validated matrix geometry, bounds, and CRT chunk planning. Strengthen exact-prefix and wide-accumulation boundary tests, and remove unused public wide-ring helpers.
* ci(profile): unify fold report layout
Join planner choices, witness geometry, and proof costs in one row per fold. Emit group-specific records for multi-group roots and setup offloads so distinct commitment parameters remain visible.
Keep percentage deltas for measured costs and exact merge-base values for discrete planner choices.
* chore(pr375): complete validation slice
Repair the explicit D64 root commitment benchmark without weakening production schedule checks. Record one hot route decisions in profile reports and validate the parser against the isolated CI artifact.\n\nReplace the optimization book stub, align backend and ownership docs, and close the PR 375 spec with benchmark, compatibility, and lifecycle evidence.
* fix(prover): close independent review gaps
Preserve exact NTT operation requirements through backend routing, join retained cache requests by physical owner, and batch homogeneous projection groups through their child kernels.
Remove leaked commitment-only bounds and the streamed source wrapper, split oversized modules, add routing regressions, and archive the implemented PR specification.
* fix(profile): correct multi-group report values
* feat(profile): compare verifier thread modes
* perf(planner): retain hot suffix cache entries
Use bounded second-chance eviction within the existing direct and prefixed suffix quotas. A 700,000-insertion census reduced recomputations from about 218,000 to 145,179 without changing candidate coverage.\n\nShare setup and payload frontier keys only in states that retain both projections, and use binary search over monotone SIS rank rows. The exact planner and unpruned oracle suite passes all 21 tests.
* refactor(profile): clarify fold report cells
Group planner, work, and proof details into labeled blocks while preserving the side-by-side fold comparison. Keep multi-group precommitment, final-group, and setup-offload parameters visibly separate.
* perf(planner): bound recursive split frontier
Keep recursive split enumeration exact through twelve reduced variables. For larger multi-candidate suffix states, retain both extremes and a five-point window around the analytic balance point. Ordinary single-winner direct search still checks the complete split domain.
Move prefix-only rejection before child recursion, avoid retaining unused prefix frontiers, and compact present setup lengths with NonZeroUsize. Regenerate both recursive catalog families.
This intentionally trades global optimality for large frontier states for a practical, explicit search policy. Full 14-family generation now completes in 36 seconds.
* fix(sis): restore generated table identity
Reconnect the split artifact digest test and make the generated digest constants the single runtime source of truth. Refresh the q128 Inner/512 extension digest for the checked-in rows and regenerate every catalog identity.
Also replace the compression planner's manual loop counter so all configured CI Clippy graphs pass.
* fix(profile): remove persisted bench setup
Construct recursive benchmark setup in memory for every sample instead of enabling disk persistence. This prevents public matrix and prefix registry caches from entering per-group handoff artifacts and makes setup timing measure fresh construction.
* fix(prover): preserve i16 opening digits
* docs(pr375): reopen CPU policy spec
Define configurable CPU resource limits on the existing backend, complete NTT release semantics, and the downstream Jolt validation slices.
* refactor(planner): resolve review findings
* feat(compute): configure CPU resource limits
* fix(planner): preserve terminal basis winners
* fix(compute): release all CPU NTT caches
* fix(compute): retain compression NTT cache
* feat(profile): report CPU resource limits
* perf(ntt): use exact tail for quotients
Route centered relation quotients through the existing five-prime CRT prefix plus the 12289 exactness tail when one base term cannot fit. Prewarm only the affected A prefixes and keep scalar arithmetic as the final capacity fallback.
Parallelize independent exact-i16 commitment blocks and restore the advertised setup-first objective for direct grouped schedule generation.
* test(ntt): guard small-field cache plans
Lock the fp32 and fp64 dense nv26 schedules to their ordinary cached NTT domains and verify that catalog planning agrees with field-typed runtime tail selection across every modulus profile.
Remove the stale profile label that described shipped D128 catalogs as runtime-DP schedules.
* ci(profile): clarify dense benchmark suite
Rename the existing three-field dense benchmark leg instead of duplicating runner work.
Show the resolved A/B/D fold schedule in compact reports and update the active profiling documentation to explain why fp128 uses D256 only for the root A role.
* refactor(planner): enumerate exact role tuples
* feat(config)!: adapt small-field schedule profiles
* feat(planner): adapt direct grouped schedules
* feat(config): expand small-field dimensions
Certify and dispatch fp64 through D512 and fp32 through D1024 for the inner commitment role. Keep outer and opening search bounded at D256, regenerate every affected schedule family, and cover the exact NTT requirements and runtime dispatch paths.
* fix(catalog): harden adaptive runtime wiring
Unify dispatch, SIS coverage, runtime capability, and generated-family ownership around canonical policy tables. Reject non-monotone terminal dimensions, remove dead runtime obligations and duplicate routing, strengthen drift/oracle coverage, and align live documentation.
* fix(ntt): fail closed on unsafe streaming
Reject ring-switch work that exceeds the streamed CRT capacity before entering the retained-cache path. This preserves the configured memory bound and adds a regression proving no cache slot is built on failure.
* test(sis): align role-specific D512 coverage
* test(pcs): dispatch adaptive opening dimensions
* fix(pcs): generalize adaptive opening helper
* test(config): clean all-schedules feature graph
* fix(profile): close benchmark report review gaps
* refactor(planner): centralize admission and pruning
Route precommit admission, scalar sizing, Pareto insertion, recursive split traversal, and generated-family providers through their canonical owners. Harden descriptor validation, entropy coverage, SIS limits, output isolation, and exact i16 composition tests in response to review.
* fix(profile): report benchmark shards exactly
Record each workflow shard in benchmark artifacts, render field-first shard ownership separately from public opening statements, and make partial merge-base coverage explicit so missing base modes never produce false deltas.
* fix(prover): resolve routed cache review findings
* fix(ci): align recursive profile linkage
* fix(ci): check adaptive profile linkage
* fix(config): drop stale recursive import
* fix(ci): align recursive profile linkage
* fix(ci): restore balanced nextest shards
* test(pcs): pin adaptive W8R2 schedule shape
* fix(profile): address benchmark review follow-ups
Resolve schedule features transitively across crate boundaries and derive profile linkage allowlists from that canonical graph.\n\nThread the exact final group layout into benchmark reporting, reject incomplete public group metadata, and simplify compact report tables.\n\nAlso make workflow-step test failures explicit.
* fix(ci): isolate recursion build cache
Key the recursion smoke target cache by the exact source revision. This prevents path dependencies outside the recursion workspace from restoring stale artifacts across PRs.
---------
Co-authored-by: Markos Georghiades <mgeorghiades@a16z.com>
Co-authored-by: sumchecker <241190306+sumchecker@users.noreply.github.com>
Summary
Add six narrow
akita-pcsprofile feature groups and condition the profile mode registry so release builds only monomorphize the concrete PCS configurations selected by that feature. The existingprofile-cifeature remains the compatibility union.This PR only adds the capability; the next stacked PR adopts it in CI.
Why
The profile registry held function pointers for all 11 CI modes. That made every configuration reachable, so rustc monomorphized all of them even when a matrix cell ran only one mode. In the fresh slow CI job, profile binary builds took about 4m47s–5m15s, while the benchmark itself took only about 29s.
Expected improvement
Cold local release measurements:
parallel,profile-ciunionThe dense group is 61% faster and produces a 56% smaller binary than the full union locally. Single-mode CI cells should see a similar order-of-magnitude reduction once the next PR adopts these features; multi-mode groups should land between the singleton and union results.
Validation
cargo checkwith-D warningsfor all six new groups,profile-ci, and the singleton featurecargo fmt --all --checkStack