Skip to content

History

Revisions

  • The int4 clock term is a STEP not a slope: retire the 19% underclocking figure, and record a silent miscompute after a refused dispatch

    @mafischer mafischer committed Oct 13, 2026
    4a79260
  • Move the INT4 BCHAIN operational notes out of the registry row and onto a page of their own

    @mafischer mafischer committed Oct 13, 2026
    3df7ab4
  • The mode-4 premise is board-dependent: a one-board census was read as a verdict, and shipped wrong output for six commits

    @mafischer mafischer committed Oct 13, 2026
    61bb186
  • Index the last two days' pages — four of them were orphans reachable only by guessing the filename

    @mafischer mafischer committed Oct 13, 2026
    1432859
  • Board-validation attest records which board, and test_speed's latency guards go per board

    @mafischer mafischer committed Oct 13, 2026
    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.

    @mafischer mafischer committed Oct 13, 2026
    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.

    @mafischer mafischer committed Oct 13, 2026
    b831801
  • Exp-2026-10-12 INT4 BCHAIN: section 27 - the dense activation pack re-measured on the post-completion-gate tree and landed (3e5d399)

    @mafischer mafischer committed Oct 13, 2026
    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

    @mafischer mafischer committed Oct 13, 2026
    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.

    @mafischer mafischer committed Oct 13, 2026
    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.

    @mafischer mafischer committed Oct 13, 2026
    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

    @mafischer mafischer committed Oct 13, 2026
    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

    @mafischer mafischer committed Oct 13, 2026
    e4cd4b4
  • int4 BCHAIN E2: the activation address generator decoded, a bit-exact dense pack, and why the 4x padding is not removable

    Michael Fischer committed Oct 13, 2026
    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

    @mafischer mafischer committed Oct 13, 2026
    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.

    @mafischer mafischer committed Oct 13, 2026
    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.

    @mafischer mafischer committed Oct 13, 2026
    daeff1d
  • Exp-2026-10-12: the multi-block wall is per-program capacity, and where the 3.5 ms goes

    @mafischer mafischer committed Oct 13, 2026
    b46b3d3
  • Exp-2026-10-12: INT4 BCHAIN dispatch envelope is Wb, not H

    @mafischer mafischer committed Oct 12, 2026
    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.

    @mafischer mafischer committed Oct 12, 2026
    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.

    @mafischer mafischer committed Oct 12, 2026
    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.

    @mafischer mafischer committed Oct 12, 2026
    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.

    @mafischer mafischer committed Oct 12, 2026
    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.

    @mafischer mafischer committed Oct 12, 2026
    3f3aea6
  • The native-W4A4 prefill gap is program count, not submits, and Wb dominates H 43 to 19

    @mafischer mafischer committed Oct 12, 2026
    8c2f31c
  • .236's A76 2400 MHz ungating lives only in its installed DTB, so any rebuild loses it

    @mafischer mafischer committed Oct 12, 2026
    903504c
  • GPTQ is free and worth 12 percent; SmoothQuant costs 7 percent speed and 3.8 percent PPL here

    @mafischer mafischer committed Oct 12, 2026
    b0ec4af
  • Scatter beats contiguous 1.92x once resets are free; the cause is K=6144 weights at H=3

    @mafischer mafischer committed Oct 12, 2026
    6b3f81b
  • Revalidated on today's kernel: the I4_CHAIN-I8_CHAIN reset is no longer load-bearing

    @mafischer mafischer committed Oct 12, 2026
    050a70d
  • Contiguous promotion cuts 221 resets to 4 and prefill 9.12 -> 50.83, with no policy change

    @mafischer mafischer committed Oct 12, 2026
    0e61108