The int4 clock term is a STEP not a slope: retire the 19% underclocking figure, and record a silent miscompute after a refused dispatch
4a79260
Move the INT4 BCHAIN operational notes out of the registry row and onto a page of their own
3df7ab4
The mode-4 premise is board-dependent: a one-board census was read as a verdict, and shipped wrong output for six commits
61bb186
Index the last two days' pages — four of them were orphans reachable only by guessing the filename
1432859
Board-validation attest records which board, and test_speed's latency guards go per board
b78563e
The fp16 M envelope is a predicate, and the main path was silently wrong at attention's K
Started as a performance question about the stream pair and turned out to be a
correctness bug in ork_f16_mm_run itself: at K=128 N=2048 M=64 it returns
max|diff| 5.12 against |C|max 2.17, with no error and no decline. It looked
correct only because the per-core column split happened to shrink Ncore below
the boundary at the N values anyone had tried, and K=128 N=n_kv is attention's
exact shape. 54 of 126 swept cells were wrong.
One rule reproduces all 126: the weight window starts at bank DATA_BANK and
runs min(wreq, WEIGHT_BANK) banks, wrapping into the activations past bank 11,
so correctness needs dreq <= DATA_BANK and DATA_BANK + min(wreq, WEIGHT_BANK)
<= 12. At well-formed K the encoding keeps the two summing to 12, which is why
this reduces to the cap already in the tree everywhere except K=128, whose
half-bank step the encoding cannot express.
Expressing it as a predicate rather than a ceiling is what made the fix
possible: three callers now respond according to what each can do -- orki_run
pads through its own scratch, colsplit narrows on the round's widest per-core
N, and the stream pair refuses because it indexes the caller's buffers. The
usable region GREW as a result; the chain now passes at every M >= 80 at
K=128, including values that were silently wrong before.
Records three open items, including that the stream path is ~100x slower than
the chain at every K whether correct or not.
554c601
Three defects in the shared doorbell drain, and why a self-healing path hides them
All three are in core/, not the int4 path; an int4 workload that misses
constantly merely walked every failure branch. The EWMA miss-window key
omitted the geometry, so a geometry the board takes and one it will not shared
a slot and the window was wrong for both. The 50ms floor sat below one poll
pass, so detection could not resolve finer than 15x the correct runtime. And
the recover re-seeded the 75% of each output slot that is padding, which left
the collision adjudicator structurally unable to accept on that path ever
again.
Records the generalisation worth keeping: a correctness-preserving recovery
path hides its own defects, because the only symptom is time. The ladder
returning a bit-exact answer is precisely what kept these invisible. Anything
that self-heals needs a timing gate as well as a correctness one.
b831801
Exp-2026-10-12 INT4 BCHAIN: section 27 - the dense activation pack re-measured on the post-completion-gate tree and landed (3e5d399)
be637db
int4 BCHAIN: no out-of-band completion witness exists — task order does not imply write-visibility order, and the drain-verify is load-bearing at production geometry
5ba0d16
The tail-row price is 1.7*Wmax us, it is paid from Wb=128, and r==1 is a dispatch hole
Fits T(h) ~ alpha + A*g + B*r over nine (K,Wb) grids with a confound-free
design: at hcap=32 the cells h=31..26 are all g=5 with r=4..24, a pure B axis,
and h=30/24/20/15 are all r=8 at g=5..9, a pure A axis. Measured B is 1.7*Wmax
and K-independent at fixed Wmax, which is the r*Wmax*64-cell scan the
mechanism predicts. The shipped gam of 1.25*Wmax was 1.8x light; 2*Wmax picks
the measured optimum at 18 of 18 M-sweep points against 16 of 18.
A is genuinely ill-conditioned at wide Wb and the reason is mechanistic: an
extra M-group adds tcrit programs but shrinks the padded mcc sync extent, and
weight reuse removes the re-stream, so what remains is a small difference of
opposing terms. Those grids are reported rather than fitted.
Records a new blocker the recalibration would otherwise have walked into: a
final M-group of ONE row is a dispatch hole at K=768 Wb=128, 0/9 across three
independent (M,h), ~15.6s doorbell miss each, where r=2 at the same h is 3/3.
The search minimises r, so it steers straight at it. Guarded.
Withdraws two earlier claims measured against themselves -- the Wb<=128 "null"
was an A/A at M where the search does not move, and Wb==64 is a 1.3% gain, not
a loss -- and notes that an 8-sample end-to-end A/B read one cell as a 1.06%
loss that is a 0.70% gain at 20.
60e77c2
The int4 completion gate: the drain-verify is the cost, and a kernel fence is slower
Phase-times the gate rather than apportioning it. Seed, flush and the poll
spin are ~4% together; the post-completion drain-verify is ~26%, serialised
across cores behind a kernel sync covering 4x the written cells and followed
by a second pass that re-reads them to de-tile.
Records that RKNPU_JOB_FENCE_OUT works on the stock driver, first try, no
reflash -- and is 13% SLOWER. The fence signals at job_done while int4's DPU
writeback lags it, so the drain-verify still runs afterwards with nothing
overlapped. It is a completion signal, not a data-visible one, and it cannot
remove the poll. Also identifies what "fencefix" in the kernel name is: a
rknpu_job_free change that signals -ETIMEDOUT on the abort path so a waiter
can tell a stall from a completion.
The shipped change takes 1.15-1.24x by parallelising the verify, invalidating
only the lines a program wrote, and fusing verify with de-tile. It is
fail-safe by construction: a stale line reads back the seed, so it can delay
completion but cannot declare it early. Correctness is graded at a calibrated
sensitivity, and states plainly that the harness cannot show the drain-verify
is load-bearing on these shapes.
Both boards now agree the tail gate should drop to Wb>=128, and the cost
model's gam term under-weights the tail at narrow Wb by more than the gate
change is worth.
d2d39b5
int4 BCHAIN: the Wb rule is shippable once ladder rung 1 is the shipped width — and .235's shipped geometry already failed at two production K
9ae2d30
int4 BCHAIN: the geometry's cost is the LAST M-group, hcap is a local worst, and the lever is created by a wide Wb
e4cd4b4
int4 BCHAIN E2: the activation address generator decoded, a bit-exact dense pack, and why the 4x padding is not removable
9a8d954
int4 BCHAIN: E1 settles the stride-2 zero lines, E2b is a dead register, and the H_race surface yields a Wb lever for K<=1024
5556eee
Correct the registers the int4 stride narrative keeps pointing at, and record the precision enum
0x405c is DPU_WDMA_SIZE_1 (height/width), not a stride in any precision, yet
four pages call it "the PPU stride" and the roadmap sent a probe at it. The
activation-side lever is 0x107c CNA_DMA_CON1.LINE_STRIDE, which the ISA
Reference already named while flagging it open; burst length belongs to
0x1078. Both entries corrected so the index stops disagreeing with itself.
Adds the precision enum derived from our own three captures -- 0=int8,
1=int16, 2=fp16, 4=int32 out, 6=int4 -- on CNA 0x100c and DPU 0x4010. No
external source carries it: mainline is autogenerated and enum-free, Mesa
writes zero and never touches a precision field, NVDLA has no INT4 row at all.
It establishes that a dedicated int4 processing mode exists on this silicon,
and separates that from the still-open question of whether the MAC array is
correspondingly wide.
Retracts Exp-2026-07-08's "no further register lever exists" -- Wb reaches 320
at K=2048 for 2.09x bit-exact, H reaches 16-64, and the 8-rows-by-1-bank lock
is not a lock. Flags two claims on Exp-2026-06-19: it records the input as
stride-1 while being cited as the origin of the stride-2 finding, and its
parity-with-INT8 prediction predates any throughput measurement.
38d84ed
int4 BCHAIN envelope: map 10.3.0.236, and the NBc>=2 wall is a race with an H floor
The one-32KB-bank capacity reading is refuted on .236 by three independent measurements:
equal byte counts fall on both sides of it, the CBUF DATA/WEIGHT split does not move it, and
raising H rescues a byte-identical per-program weight tile at NT=1. The envelope is a
(K, Wb, H) surface: Wb=64 is immune at every K from 512 to 8192, and Wb>64 needs H above a
floor that rises with K and must still sit under the activation ceiling -- empty at K=2048,
which is why the shipped Wb clamps to 64 there.
Also: .236's residual holes are K=2560 and K=3072, not K=2048; its shipped geometry is
20/20 at every K; the DMC sweep 2112/1560/1068/528 grows the envelope monotonically on
LPDDR4X; and the per-part rung memo writes no entry and costs nothing on this die.
daeff1d
Exp-2026-10-12: the multi-block wall is per-program capacity, and where the 3.5 ms goes
b46b3d3
Exp-2026-10-12: INT4 BCHAIN dispatch envelope is Wb, not H
dab3ffc
The W4A4 activation prep is 1.524x faster and it was never the rotation
Instrumenting before optimising found the cost in a different place than
anyone guessed. The t_actq counter was never incremented on the W4A4 path at
all -- both += sites are on the int8 path -- which is why it reported all-misc.
The real breakdown puts the MSE clip search at 80%, the rotation at 12-13%,
and the SmoothQuant divide at zero because this pack carries no smooth vector.
Two findings outweigh the table: the term is pure ALU at 0.29 GB/s with
L1-resident rows, so the codebase's usual move-fewer-bytes lever is wrong here
and an explicitly fused form measured slower; and the OpenMP schedule was
static across 4 A76 plus 4 A55, so the little cores at ~5x slower left every
big core idle for most of the loop.
NEON on the clip search and quantise plus schedule(dynamic) takes prefill
50.28 to 76.62 tok/s, bit-exact over 805M activation elements with PPL equal
to six digits. Records the two argmin flips found on unrotated synthetic rows
and why the weight clip was left scalar.
18152cc
End-to-end 1.128x prefill, and the storm is a wrong hcap entry, not the sentinel
First model measurement of this line: 1.128x prefill, 1.00x decode, PPL
identical to every digit. The per-call lever did not decay -- on real shapes
it GREW to 1.3499x, so the microbenchmark understated it by 1.12x and the
shortfall is pure Amdahl. The census then kills the geometry lever: K<=2048 is
79% of W4A4 run time but only 25.7% of prefill wall, so its realistic payoff
is 1.05-1.09x against a board-wedging storm.
Root-causing that storm on .235 refutes the sentinel hypothesis as its cause
while confirming the mechanism exists and is forceable. The real cause is that
orki_i4_hcap(2048) ships 8 where that die tops out at 4, so .235 storms 100%
of the time at the SHIPPED default and has been paying ~5.2s per int4 prefill
matmul behind a ladder rescue that always returns the right answer.
Names the next target from the same profile: the CPU-side W4A4 activation
rotate and quant is 33.1% of prefill wall, as large as the entire NPU run, and
is completely unattributed.
bc0457d
Where the int4 time actually goes: NPU dispatch 72 percent, and two instrumentation lies
The DMA accounting compared non-fungible quantities. Measured attribution puts
NPU execution at 72-73% and host regcmd synthesis at 11-14% -- four to eight
times the nibble-pack that section 9 credited with deciding the transposition
A/B. Neither operand's DMA sets the clock: weight bandwidth is 0.55-0.85 GB/s
against a ~30 GB/s ceiling.
Records two pieces of instrumentation that mislead. The grow-only scratch also
has fixed pre-allocation floors, so even a single-shape process over-syncs
2.4-3.0x and orki_bsync_live is worth 8.3% at the geometry worth shipping.
ORK_DOM_PROFILE starts its timer after the drain barrier and so reports
0.97 us/swap against a real ~300 us.
Keeps the open tension on section 9's attribution rather than smoothing it:
that A/B had matched program counts, so program count cannot explain it.
47ea096
Transposition is refuted: the operand roles are not symmetric in software
Swapping the matmul so the small matrix holds the weight role is expressible
and measurably slower -- 3106 us untransposed against 3642 us swapped at
matched split, Wb, programs and cores, while moving half the bytes. These
shapes are bound by neither DMA nor program count; the weight role is packed
once while the activation role is nibble-packed on the host every call, so
transposing raises per-call host work 16x.
Confirms in the same run that the Wb WRONG boundary vanishes with reuse off,
so the ceiling is weight-reuse staleness rather than containment. Records two
hazards: the WRONG points were the fastest in the sweep, and geometry validity
depends on core count, so a Wb rule keyed on K alone is unsound.
7babcc1
Wb can be raised 2.21x at K<=2048, but the pair is H, not the bank count
Measures the three predictions section 7 left open. Two are refuted: n(12-n)
is the wrong objective (the binding term is activation DMA, a function of Wb
alone), and transposition is net negative (the weight already streams once,
and the core clamp would cap a transposed prefill at 2 cores). K-slicing is
arithmetically right but 4.5x slower below 2048; slicing to 2048 to reach the
lever pays 1.24x.
Also records that "DO NOT RAISE IT" was an artifact of an unpaired knob, that
the shipped split is already maximally weight-biased, and that ORK_I4_DBNK
overshoots H at non-dividing K.
3f3aea6
The native-W4A4 prefill gap is program count, not submits, and Wb dominates H 43 to 19
8c2f31c
.236's A76 2400 MHz ungating lives only in its installed DTB, so any rebuild loses it
903504c
GPTQ is free and worth 12 percent; SmoothQuant costs 7 percent speed and 3.8 percent PPL here
b0ec4af
Scatter beats contiguous 1.92x once resets are free; the cause is K=6144 weights at H=3
6b3f81b
Revalidated on today's kernel: the I4_CHAIN-I8_CHAIN reset is no longer load-bearing
050a70d
Contiguous promotion cuts 221 resets to 4 and prefill 9.12 -> 50.83, with no policy change
0e61108