Repository navigation
Experiment: eight-lane warm partial-comb evaluator (31% gate on Zen 5) - #1
Open
7layermagik wants to merge 10 commits into
Open
7layermagik wants to merge 10 commits into
7layermagik wants to merge 10 commits into
Conversation
The warm-comb verifier is x4-only. Its scheduling unit can already be eight signatures, but only as two aligned x4 groups run back to back: the arithmetic is two YMM groups, not one ZMM group. This adds the eight-lane evaluator. It changes no table format, no recoding rule, no schedule and no acceptance predicate; the exponent loop, pass ordering and balanced-digit semantics are the x4 ones with a wider lane count. Every kernel it calls already existed in x8 form, so no new assembly was needed. The per-key table needs no rebuild: it is a scalar table, so an x4 group owns four instances and an x8 group owns eight. Keys already promoted for the x4 path are consumed unchanged. Not wired into the verifier, and not gated yet. The differential test compares the x8 evaluator against two x4 evaluators over the same inputs across a sweep of active masks, since the x4 path is what ships and any disagreement is a behaviour change. Nil lanes are swept at every position, carrying forward the guard from the warm-comb crash fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ten one-second samples per row on a pinned Zen 5 physical core: one x8 group verifies eight signatures at a median 1,577 ns/signature against 2,287 for two x4 groups, a 31.0% reduction with the spread inside 0.5% and zero allocations on both rows. The same structural change on the cold path measured 26%, so the warm evaluator gains slightly more. The benchmarked unit is the whole evaluator -- merged exponent loop, every doubling, both comb passes, the recoding -- not a single kernel. That distinction is deliberate: the two-chain ZMM candidate passed its component gate and then regressed 66% at the loop level on Zen 4. Recorded with the limits stated rather than implied. It is an upper bound on the complete-verifier gain, since hashing, decoding and finalization sit outside the loop and do not widen; the evaluator is roughly half the warm path, so end to end is likely nearer 15%. Zen 4 is unmeasured and must not be assumed. And a true x8 warm group needs eight homogeneous warm lanes rather than four, with no partial credit, so the unmeasured fee-payer recurrence distribution may decide whether the win is reachable at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two errors in the comb-width table. It divided the 128 MiB budget by the bare comb payload, but the promoted cache entry is r51WarmTableBytes -- the comb plus the 192-byte decoded-A entry it supersedes -- so every count was overstated: A6/r9 holds 6,909 keys, not 7,158. And as already noted for the decoded tier, the byte budget is not the binding limit anyway, since admissionEntryLimit is 131,072 and applies to every tier. Also records that the two narrower rows are arithmetic rather than configuration. validate() admits widths 6 through 10 only, so A5/r9 and A4/r9 panic today; adopting either means relaxing consensus-critical shape validation, not turning a knob. Found while stating the same figures in the x8 evaluator gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decomposing the x8 warm evaluator put 16.5% of it in scalar recoding, which was unexpected for a pure bookkeeping pass. The generated assembly explains it: carry = (digit + half) / radix compiled to a hardware IDIVL in the innermost loop, executed once per digit per lane -- 344 divisions for the A term and 208 for B on every group of eight. The division is avoidable and the replacement is exact. The bit extractor yields a digit in [0, radix-1], the incoming carry is 0 or 1, and half is radix/2, so digit+half lies in [half, radix+half] and is never negative. Over a power-of-two radix a shift is then exactly the truncating division, and the compiler only emitted a division because it could not prove the numerator non-negative. Applied to the shipping x4 recoder as well as the x8 experiment, so this is a production-path change rather than an experimental one. No digit, carry, sign or mask semantics move; the existing differentials cover it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Recaptures the gate at the post-division-fix commit and adds a component decomposition that explains 89% of the evaluator. Two findings the measurement produced. Recoding was 16.5% of a pure bookkeeping pass because it compiled to a hardware IDIV per digit per lane; removing it moved the shipping two-x4 form from 2,287 to 2,229 ns/signature. And selection is not the bottleneck at 14.2% -- addition and doubling arithmetic is 62.1% -- so further large gains must come from fewer or cheaper group operations, not faster table lookup. Sizes the levers to a sub-microsecond warm path against the model. Three of them reach roughly 1,050-1,100 ns/signature at no memory cost; going decisively below one microsecond needs a wider A window, which is per-key memory, which is capacity, which is gated on the same unmeasured recurrence distribution as the comb width. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sweeps the comb shape on Zen 5 and answers whether the warm path can reach one microsecond. It cannot, by shape alone: eliminating the doubling chain entirely costs 8.6x the per-key memory, drops capacity to 811 keys, and still measures 1,133 ns/signature. The 69 additions, two recodes and 69 selections set a floor near 1,040. Reaching a microsecond needs a cheaper addition, not a different comb. Two actionable findings fall out. Only depth matters, so the largest passes count that reaches a target depth is always correct: A6/r7 and A6/r5 both measure about 1,460 with B10/r5 because both already sit under B's depth of 40, and r7 gets there for 22% less per-key memory. And the shipping pair is mismatched in the expensive direction. A6/r9's depth of 48 exceeds B10/r5's 40, so eight doublings are driven purely by the per-key side. B is one process-wide table, so rebalancing toward it -- B10/r3 with A6/r5 -- reaches depth 24 for 34,560 B/key, 12.8% faster, with the extra rows paid once rather than per key. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds an eight-lane heterogeneous partial-comb evaluator and the Zen 5 gate for it. Experiment only: nothing is wired into the verifier and no dispatch changes.
Why
The promoted warm-comb verifier is x4-only. Its scheduling unit can already be eight signatures, because
r51WarmDispatchWidthconsumes aligned pairs to keep x8 occupancy in the surrounding pipeline. But the arithmetic underneath is two sequential YMM groups, not one ZMM group. The cold path already took that step; the warm path never did.Result
Ten one-second samples per row, pinned physical core on a Ryzen 7 9700X,
GOMAXPROCS=1, performance governor:31.0% per signature, 1.45x, spread inside 0.5% on both rows. The same structural change on the cold path measured 26%, so the warm evaluator gains slightly more.
Evidence, environment and checksums:
docs/results/zen5-warm-comb-x8-gate-2026-07-26/.What the benchmark covers
The whole evaluator: the merged exponent loop, every doubling, both comb passes, and the balanced recoding. Not a single kernel.
That distinction is the point. The two-chain ZMM candidate passed its component gate and then regressed roughly 66% at the loop level on Zen 4 (
zen4-two-chain-zmm-loop-gate-2026-07-26), so a component measurement is not evidence about a loop.Cost of building it
Lower than expected:
Nothing about the table format, the recoding rule, the schedule, or the acceptance predicate differs from the x4 evaluator. The exponent loop, pass ordering and balanced-digit semantics are the x4 ones with a wider lane count.
Table sizes this consumes, unchanged
Worth stating explicitly, because the obvious worry about a wider group is that it costs more memory. It does not: the per-key table is a scalar table, so an x8 group holds eight pointers to the same 120-byte-entry tables an x4 group holds four of. Every figure below is what ships today.
Per-key A table (promoted, per warm key)
The promoted cache entry is
r51WarmTableBytes= 19,424 B, which is the 19,200 B comb plus the 192 B decoded-A entry it supersedes (32 + 4*5*8). At the 128 MiBDefaultMaxTableBytesthat is 6,909 warm keys, against 699,050 if the same budget held only decoded-A entries.Neither is the effective ceiling:
admissionEntryLimitis 131,072, so admission bookkeeping binds first. That number should not be quoted as a capacity.Shared fixed-B table (one process-wide instance)
B is pre-signed, storing both signs so the hot loop needs no post-selection negation; that is what doubles it. It is shared across every lane and every key, so the ~720 KiB is paid once per process, not per key. Per-key A tables deliberately stay single-sign, since pre-signing them would double 19,200 B on every key.
All of the above is pinned by
TestHeterogeneousPartialCombTableShapesAndPayloadsExperiment.On narrower combs.
PERF_FINDINGS§5.3 floats A5/r9 and A4/r9 as ways to buy more keys. This PR also corrects that table: it divided the budget by the bare comb payload rather than the promoted cache entry, so every count was overstated (A6/r9 holds 6,909 keys, not 7,158), andadmissionEntryLimitbinds before the byte budget on every tier anyway. The two narrower rows are also arithmetic rather than configuration —heterogeneousPartialCombSpecExperiment.validate()admits widths 6 through 10 only, so both panic today. Whether either is worth a slower warm path still depends on the unmeasured fee-payer recurrence distribution, and this PR takes no position on it.Correctness
The differential compares the x8 evaluator against two x4 evaluators, deliberately, rather than against a scalar reference: the x4 path is what ships, so a disagreement is a behaviour change whichever side is wrong.
0xff,0x0f,0xf0,0b10110101, a single low lane, a single high lane, and zero;Both pass on the Zen 5 host;
correctness.txtin the evidence directory is that run.What this does NOT establish
Commits
d42d75d— the eight-lane evaluator and its differential testsa0604ad— the Zen 5 gate evidence bundle9edbf2c— thePERF_FINDINGScapacity-arithmetic correctionSuggested next step
Wire it behind the existing
PreferWarmX8IFMApolicy bit and run a complete-verifier A/B through the public API, plus a Zen 4 gate before any dispatch change.🤖 Generated with Claude Code