Skip to content

perf(dspark): unblock upstream ncu; the DRAM attribution is CORROBORATED (#442) - #562

Open
localai-bot wants to merge 15 commits into
mainfrom
row/SPEC-DSPARK-MARLIN-NCU
Open

perf(dspark): unblock upstream ncu; the DRAM attribution is CORROBORATED (#442)#562
localai-bot wants to merge 15 commits into
mainfrom
row/SPEC-DSPARK-MARLIN-NCU

Conversation

@localai-bot

@localai-bot localai-bot commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #550 (same row, same issue).

§6z recorded upstream's ncu counters as the last open route and marked it BLOCKED — vLLM's EngineCore will not initialise under ncu in either replay mode — so its DRAM efficiency stayed derived rather than counted, and the spec named a standalone harness as the only way in.

scripts/marlin-moe-standalone.py is that harness: it drives upstream's own torch.ops._moe_C.moe_wna16_marlin_gemm on the 35B-A3B decode shapes (hidden 2048, moe_intermediate 512, E=256, top_k=8, moe_block_size 8) with no EngineCore, no multiprocessing and no model load, mirroring prepare_nvfp4_moe_layer_for_marlin's scale pipeline exactly. It asserts the oracle identity and aborts on mismatch. It profiles.

Static geometry (new, and trustworthy — these are static properties, not counters)

grid 144, block 128, 32768 B shared per block. GB10 has 48 SMs with 102400 B of shared memory each → 3 blocks/SM → 25% occupancy (achieved 25.98%), and 48 × 3 = 144 is the grid. The launch is a persistent single wave, so the "38.9 vs 40.6 blocks per launch" of §6x are work items each CTA loops over, not parallel blocks.

A measurement trap worth the commit on its own

dram__bytes.sum reads n/a on GB10 — the chip exposes no DRAM counters. SpeedOfLight still prints "Memory Throughput 11.14%" beside "Compute (SM) 11.42%", and reading those as "neither saturated, so latency-bound" is wrong: that memory figure excludes DRAM traffic entirely. An n/a on the byte counter is the tell that the percentage beside it comes from somewhere else. The second commit on this branch retracts exactly that misreading — the branch deliberately keeps both the wrong reading and its retraction rather than being force-pushed.

What actually answers it: sweep the work

distinct-expert pool blocks µs/call µs/block implied GB/s
8 13 19.9 1.53 fits L2
16 18 20.8 1.15 fits L2
24 22 65.0 2.96 transition
32 27 157.0 5.81 202.9
48 38 207.0 5.45 216.6
64 45 249.5 5.55 212.7
128 58 306.5 5.28 223.2
256 65 339.6 5.22 225.8

Each block streams 1179648 B (1 MiB of weights + 128 KiB of scales). Two regimes: under ~18 blocks the weights fit in L2; above ~27 they stream and µs/block is flat at 5.2–5.5 across a 2.4× range of work. Constant bytes-per-second across varying work is the bandwidth-limited signature. Plateau: 203–226 GB/s.

So §6z was right

Its derived in-situ numbers land on that plateau. Upstream's 210.7 GB/s is inside it — upstream runs at this kernel's achievable bandwidth — while ours at 186.6 GB/s is ~12% below. The harness built to test the attribution corroborates it instead of overturning it.

What does not survive is §6x's per-unit-work division: with a fixed 144-CTA grid, block count is loop iterations, and the sweep prices 38.9 → 40.6 blocks at about +4.4%.

Open, and decisive

Ours has not been through this harness. That is the next measurement: our kernel, same shapes, same routing, µs/block against the 5.2–5.5 plateau. If ours plateaus there, §6x's localisation to this kernel is wrong; if ~12% higher, the kernel owns the gap. Occupancy stays a real but secondary lever at ~75–80% of this device's ~273 GB/s.

Absolute µs/call here is not comparable in-situ (uniform synthetic routing occupies 61–65 blocks vs the model's 38.9–40.6); the geometry, regime shape and plateau bandwidth are.

Gate: scripts/agent-preflight.sh --stagedAll gates green.

FOLLOWING_AGENTS_PROTOCOL

mudler added 4 commits August 12, 2026 23:23
…ions (#442)

The developer asked whether the weights sitting on NAS, or not being fully
resident, could be distorting these measurements. Tested and refuted: the
weights are on local NVMe (no NAS mount exists on the box), a run reads 22.06 GB
once at load, decode-time RSS is 4.8 GB because the mapping is released after
upload, and 8 warm reps hold a 0.5% spread. File-backed weights could not
produce that stability, and on GB10 the failure mode would be loud anyway --
host pages reach the GPU through ATS at a measured 20-30% per-GEMM penalty.

Recorded alongside it: that NVMe is 98% full (76 GB free). This repo has
already lost a gate run to ENOSPC reporting green over work that never ran.

Also records a third WITHIN-session paired ratio, ours -> oracle -> ours at free
clocks with drift bracketed at -0.89%: 140.98 vs a modal 147.32, RATIO 0.9569.
With the two pinned-clock pairs (0.9757, 0.9646) that puts the gap at
~0.966 +/- 0.01, consistently below 1.0, while the ABSOLUTE numbers move up to
5% between sessions for the same binary because GB10's memory clock cannot be
pinned. Only the within-session ratio is quotable, and all three agree. The
oracle's draws remain bimodal (~147.3 vs ~155.6) from the same one-extra-
accepted-token effect, so its modal draws stay the honest denominator.

No code change; this is the record catching up with the measurements.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
Picks up the hf_snapshot.h build repair (#556) so this record-only PR builds.
Its CPU, sanitizer and Windows-compile jobs were red for that reason alone --
the branch changes four markdown files and cannot affect a build.

No overlap: #556 touches tests/parity/hf_snapshot.h, this branch touches the
DSpark spec, the benchmark record, STATUS and BENCHMARKS.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…tion (#442)

Section 6z listed upstream's ncu counters as the last open route and recorded it
BLOCKED: vLLM's EngineCore will not initialise under ncu in either replay mode,
so its DRAM efficiency stayed DERIVED rather than counted, and the spec named a
standalone harness as the only remaining way in.

scripts/marlin-moe-standalone.py is that harness. It drives upstream's own
torch.ops._moe_C.moe_wna16_marlin_gemm on the 35B-A3B decode shapes -- hidden
2048, moe_intermediate 512, E=256, top_k=8, moe_block_size 8 -- with no
EngineCore, no multiprocessing and no model load, mirroring
prepare_nvfp4_moe_layer_for_marlin's scale pipeline exactly. It asserts the
oracle identity and aborts on mismatch, because this repo has three times
measured a rollback venv believing it was the pin.

It profiles. And the counters refute what 6z concluded from them:

  Memory Throughput      11.14% of peak
  Compute (SM)           11.42% of peak
  Occupancy              25% theoretical / 25.98% achieved
  Block limit            shared memory = 3
  Waves per SM           1
  grid / block / shmem   144 / 128 / 32768 B

Both throughputs at ~11% is the LATENCY-bound signature. A kernel at 11% of
memory peak is not against a bandwidth wall, so "we sustain 186.6 GB/s against
upstream's 210.7, and that IS the per-unit-work difference" does not hold --
those numbers were time x analytic bytes, so they restated the time gap instead
of explaining it.

The geometry shows the real limiter. GB10 has 48 SMs with 102400 B of shared
memory each; the kernel takes 32768 B per block, giving 3 blocks/SM and 25%
occupancy, and 48 x 3 = 144 is exactly the grid. It is a persistent single wave.
So the per-unit-work normalisation in 6x is also invalid: 38.9 vs 40.6 "blocks
per launch" are loop iterations inside a FIXED 144-CTA grid, not parallel work,
and cost is set by the CTA with the most work. ncu flags exactly that, plus
uncoalesced access at 20.2 of 32 bytes per load sector.

What does NOT change: the 3.4% gap itself. Our kernel through this same harness
on identical routing is not yet run, and no claim about the gap may rest on an
upstream-only measurement. What changes is its attribution.

What this opens: ~9x of absolute headroom that BOTH engines leave on the table,
since occupancy is capped purely by a shared-memory budget Marlin computes as
max_shared_mem / blocks_per_sm - 1024. Under 25600 B would allow 4 blocks/SM.
Not a parity lever -- upstream pays it identically -- a best-in-class one.

The synthetic router draws uniformly over 256 experts and so occupies 61 blocks
against the model's 38.9-40.6; absolute us/call from this harness is therefore
NOT comparable in-situ, while the static geometry and throughput percentages are.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…tion stands (#442)

The previous commit on this branch read ncu's SpeedOfLight line -- "Memory
Throughput 11.14%" beside "Compute (SM) Throughput 11.42%" -- as neither
resource being saturated, and concluded the Marlin kernel is latency-bound and
6z's DRAM attribution refuted. That is wrong, and the tell was in the same
output: dram__bytes.sum reads n/a on GB10. The chip exposes no DRAM counters, so
that memory percentage EXCLUDES DRAM traffic and cannot support any claim about
being far from a bandwidth wall.

Sweeping the occupied block count settles it without those counters. Under ~18
blocks the touched expert weights fit in L2 and cost 1.15 us/block; above ~27
they stream and us/block is FLAT at 5.2-5.5 across a 2.4x range of work.
Constant bytes per second across varying work is the bandwidth-limited
signature. At 1179648 B per block (1 MiB of weights + 128 KiB of scales) that
plateau is 203-226 GB/s.

6z's derived in-situ numbers land ON it. Upstream's 210.7 GB/s is INSIDE the
plateau, so upstream runs at this kernel's achievable bandwidth; ours at
186.6 GB/s is ~12% below. The standalone harness CORROBORATES the attribution it
was built to test rather than overturning it.

Two things do change. 6x's per-unit-work division does not survive a fixed
144-CTA persistent grid, where block count is loop iterations and the sweep
prices 38.9 -> 40.6 blocks at about +4.4%. And the "~9x of absolute headroom"
claimed by the retracted commit was an artefact of the same unusable percentage:
at ~75-80% of this device's ~273 GB/s, occupancy is a secondary lever.

The geometry findings are unaffected -- they are static properties, not
counter-derived: grid 144 = 48 SMs x 3 blocks, 32768 B shared per block against
102400 B per SM, 25% occupancy, one persistent wave.

Corrected in place rather than force-pushed, so the branch keeps both the wrong
reading and its retraction.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
@localai-bot localai-bot changed the title perf(dspark): unblock upstream ncu, and REFUTE the DRAM-bound attribution (#442) perf(dspark): unblock upstream ncu; the DRAM attribution is CORROBORATED (#442) Aug 13, 2026
mudler added 5 commits August 13, 2026 01:26
…EFUTED (#442)

6ad built the upstream arm of the standalone harness. This is the arm it was
built for. benchmarks/marlin_moe_standalone.cpp drives OUR
vt::MoeGroupedGemmNvfp4Marlin through the same 35B-A3B gate_up shapes, the same
expert-pool control over the occupied block count, and the same GEMM as the
python arm, so the two kernels can be asked to do the same work on the same box.

Ours plateaus on the SAME 5.2-5.5 us/block band as upstream. Two INTERLEAVED
paired runs at pool 48 and 128, three reps each:

               n    mean us/block    sd       range
  ours         12   5.3187           0.124    5.083-5.562
  upstream     12   5.3330           0.151    5.042-5.560

ours/upstream = 0.9973, ours 0.27% FASTER, inside one standard deviation on
either side, with the sign of the difference flipping between runs. A per-call
workspace memset our arm pays and upstream's does not was isolated behind
--zero-ws 0 and is noise.

So 6x does not survive. Its "the SAME kernel, the SAME 1520 launches, ours
249.22 ms vs upstream 230.39 ms, 8.2% slower inside one kernel" does NOT
reproduce when both kernels are driven with matched work. The "12.8% slower per
unit of work" and the 186.6-vs-210.7 GB/s reading derived from it therefore
describe the in-situ RUNS, not the kernel: both engines reach the same 203-226
GB/s plateau when asked to do the same thing.

What the harness does establish, and it points somewhere new: time is set by how
many DISTINCT EXPERTS a launch touches, 1.15 us/block at 16 experts where the
weights fit L2, rising to ~5.3 above ~27 where they stream. That is a 4.6x swing
no kernel change causes. Blocks are not experts, so the recorded 38.9 vs 40.6
blocks per launch never settled which arm did more work. Measuring distinct
experts per launch on both arms IN SITU is the next step, not another kernel
lever.

docs/STATUS.md's DSpark narrative is collapsed onto the binding result rather
than extended, which is what the ratchet asks for when a finding supersedes the
story that preceded it.

Caveats, stated rather than buried: our arm links ~/work/pr234's build, whose
vendored marlin_mm_moe.cu is byte-identical to current main (md5
85c40e4869bc6ec594b8cfb97fb58b3c) while its dispatcher predates the C_tmp cap,
independently measured perf-neutral at +0.03%. Ours times with steady_clock over
80 iterations plus a final sync, upstream's with CUDA events; both amortise
launch overhead, and the memset probe bounds that class of difference at noise.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
#442)

6ae cleared the kernel by showing ours and upstream's Marlin are the same to
0.27% at matched work. This says what the in-situ 8.2% was actually measuring.

Holding the block count roughly fixed while varying how many DISTINCT experts
the routing touches:

  M    blocks  distinct  us/call  us/block
  64      73      20       89.4     1.22
  64      82      40      227.1     2.77
  64      94      80      427.2     4.55
  64     216     216     1121.2     5.19
  128    137      20      149.9     1.09
  128    146      40      220.0     1.51
  128    255     250     1298.6     5.09

Read the M=128 rows: 137 -> 146 blocks is +6.6% of blocks and +46.7% of TIME,
because distinct experts went 20 -> 40. Cost per DISTINCT EXPERT is flat at
5.2-5.7 us across the whole table (1.125 MiB each, ~205-225 GB/s, the same
plateau as 6ad); cost per BLOCK varies 4.7x over the same rows.

So time is distinct_experts x 1.125 MiB / ~215 GB/s, and block count is nearly
irrelevant. Every in-situ comparison in 6x and 6y normalised by BLOCKS -- 38.9
ours against 40.6 upstream -- which is not the driver. Blocks are what
num_tokens_past_padded reports; experts are what the kernel streams. With
block_size_m = 8 and correlated spec-decode tokens one expert routinely spans
several blocks, so the two come apart.

Apply the model to the recorded in-situ launches: ours 164.0 us implies ~30
distinct experts, upstream 151.6 us implies ~28. About TWO distinct experts per
launch reproduces the entire 8.2% with ZERO implementation difference. It also
explains why both in-situ arms beat the standalone plateau per block (4.21 and
3.73 against ~5.3) -- in situ several blocks share an expert, so fewer bytes are
streamed per block. That was never a sign of anything being wrong.

Consequences. The 8.2% "kernel gap" is an artefact of comparing two different
routing draws and should not be quoted again. Any future MoE comparison must
control distinct experts per launch or force both arms onto an identical token
stream, because block counts compare the wrong quantity. VT_MOE_PAD_STATS counts
padded tokens and blocks; it should count distinct experts too, since that is
the number that predicts the time.

What this does NOT do: it does not move the end-to-end ratio, which is
wall-clock on matched prompts and token counts and stands at ~0.966. It removes
the ATTRIBUTION of that residual. The open question is whether our token path
systematically touches more experts per step -- a near-tie divergence
consequence rather than a defect -- or whether the remaining wall-clock sits
outside the MoE entirely.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…was inverted (#442)

The previous commit inferred from the distinct-experts model that our in-situ
launches touch ~30 distinct experts against upstream's ~28, and that this
reproduced the 8.2%. That is BACKWARDS, and the counts contradicting it were
already in .agents/benchmark-record.md: the 2026-08-12 both-sides measurement
recorded ours at 311.2 padded tokens / 38.9 blocks per call against upstream's
324.8 / 40.6. I inferred where the file already had a measurement.

At M=9 the 72 (token, expert) pairs spread over ~39 experts at under 8 tokens
each, so moe_align emits ONE block per expert and blocks and distinct experts
COINCIDE at the decode shape. That measurement was therefore already counting
experts, upstream touches MORE of them, and the routing explanation stays
refuted exactly as 6y concluded.

The distinction is still worth keeping, because blocks and experts come apart
4.7x at M=64/128 -- it simply does not bite at M=9.

What survives is sharper than what it replaces:

  standalone, matched work   ours == upstream to 0.27%
  in situ                    ours does LESS work (38.9 vs 40.6 expert-blocks)
                             and takes MORE time (164.0 vs 151.6 us)

A kernel identical in isolation cannot be slower in place because of its own
code, so the deficit belongs to the CONTEXT, not the kernel and not the routing.
Candidates in the order their evidence justifies: expert-weight residency in
situ, where this repo has already measured 20-30% per GEMM for host/ATS-retagged
decode weights and the standalone arm's fresh cudaMalloc cannot reproduce it;
clock and power state across the two runs; and overlap with concurrent stream
work.

Measurement-base caveat now stated: in-situ per-launch times are summed profiler
kernel durations, standalone are wall-clock over 80 iterations, and both in-situ
arms beat the standalone plateau per unit work (4.21 and 3.73 against ~5.3),
which may be partly that rather than physical.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
#442)

Two arithmetic corrections close this thread.

THE MIX. The 1520 in-situ launches are 760 gate_up plus 760 down, and down's
per-expert bytes are exactly HALF gate_up's: gate_up is 2N x K/2 weights plus
2N x K/16 scales = 1.1250 MiB, down is K x N/2 plus K x N/16 = 0.5625 MiB,
mixed average 0.8438 MiB per expert-block. 6af compared a MIXED in-situ average
against a gate_up-ONLY standalone plateau, which is the whole reason both arms
appeared to beat it at 4.21 and 3.73 us against ~5.3. They did not.

Redone with the right bytes:

               blocks   MiB/launch   us      implied GB/s
  ours          38.9      32.8      164.0       209.9
  upstream      40.6      34.3      151.6       236.9

Measured standalone gate_up plateau: 203-226 GB/s. Ours sits INSIDE it, upstream
ABOVE it. We run this kernel at the bandwidth it achieves in isolation, and
upstream gets something in place that the isolated kernel does not -- cache reuse
across the gate_up/down pair is the first candidate, down's weights being half
size so more of the working set persists. The framing inverts: on this evidence
we are not slow here, upstream is unusually fast in situ.

THE MODE HOLE. 6y warns, correctly, that "a work COUNT may be taken under
different execution modes; a TIME may not" -- and the per-unit-work normalisation
then does exactly what that forbids. The 38.9/40.6 counts were taken EAGER (ours
VT_SPEC_DECODE_GRAPH=0, upstream enforce_eager, because neither probe survived
capture and compile) while the 249.2/230.4 ms times came from the GRAPHED
profile. Nothing establishes the graphed runs had the same blocks per launch as
the eager ones, so every ratio dividing those times by those counts -- 4.21 vs
3.73 us/block, 12.8% per unit of work, 186.6 vs 210.7 GB/s -- has a denominator
from a different execution mode than its numerator.

CONSEQUENCE: the only like-for-like Marlin comparison in evidence is the
standalone one of 6ae, and it says parity at 0.9973, inside one standard
deviation. Closing the in-situ question needs blocks AND time from the SAME
graphed run, which needs a probe that survives capture: a device-side counter
the launcher increments, read once at the end, never a per-launch D2H sync.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…ntly resolve 3% (#442)

moe_wna16_marlin_gemm's last parameter is blocks_per_sm, and both engines pass
-1 (auto), which yields 32768 B of shared memory, 3 blocks/SM and 25% occupancy.
With the kernel running at ~78% of peak bandwidth, forcing a higher value was
the one settable occupancy lever left. It does not work.

A TRAP FIRST, because it nearly produced a false result. The unseeded sweep read
bps -1/1/4 slow and 2/3/5/6/8 fast, an apparent 8.7% win. The routing was redrawn
per run, so `distinct` moved between 35 and 41, and since time tracks distinct
experts (spec 6af) the sweep was measuring the DRAW. Any MoE comparison that lets
routing vary between arms measures the draw and not the change. The harness now
seeds torch explicitly for that reason.

SEEDED, identical 33 distinct / 33 blocks everywhere, 4 interleaved reps at 120
iterations:

  bps        runs (us)                          mean excl. outliers
  -1 auto    191.3, 367.8, 177.2                184.2
  3          181.1, 171.1, 177.6, 353.0         176.6
  4          177.7, 178.6, 179.8, 180.3         179.1
  5          181.7, 184.2, 179.4                181.7

Between-configuration spread is 4.3%; the WITHIN-configuration spread is
comparable (bps=3 alone ranges 171-181). No reliable effect. The lever is dead.

THE MORE USEFUL FINDING is the environment. Two of fourteen runs returned ~2x
(367.8 and 353.0) and two produced no output at all. A box emitting 2x outliers
cannot resolve a 3.4% question, and this one is currently contended: a concurrent
session has been building and rebuilding a ~79G tree on the same hardware. No
parity measurement taken in this window should be trusted in either direction.
The paired numbers earlier in the record predate that contention and were
interleaved, which is what makes them survivable.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
@localai-bot
localai-bot changed the base branch from row/SPEC-DSPARK-LOADER to main August 13, 2026 02:30
mudler added 2 commits August 13, 2026 02:31
#550 landed squashed, so its content arrives on main as a new commit while this
branch already carried it plus everything since. All three conflicts are that
shape -- HEAD is the superset in every hunk -- so HEAD is taken wholesale, which
is what the record-conflict rule asks for.

Verified rather than assumed: all 317 `## ` entries on main's
.agents/benchmark-record.md are present here, so the union lost nothing.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…lock file (#442)

Every standalone run in this branch was wrapped in `flock /tmp/gpu.lock`. That
is the wrong file. This box's GPU lock is `$HOME/gpu.lock` -- it is what
final_pair.sh takes and what other sessions hold -- so /tmp/gpu.lock coordinated
with nothing and those runs executed unserialised against whatever else was on
the GPU. At least one concurrent test_qwen27_spec_decode_concurrent (15.9 GB
RSS) overlapped them.

That is the likeliest source of the ~2x outliers (367.8 and 353.0 us) in the
blocks_per_sm sweep, and it was MINE. An earlier note in this session blamed a
concurrent session for not locking; the reverse is true -- they took the correct
lock and I did not.

WHAT SURVIVES: the ours-vs-upstream comparisons were INTERLEAVED within a single
run, so contention lands on both arms alike and the RATIO is exactly what
interleaving protects. 0.9973 with the sign flipping between reps still reads as
indistinguishable, with wider error bars than the quoted sd suggests.

WHAT DOES NOT: absolute us/call and us/block, including the 203-226 GB/s
plateau, are upper bounds rather than exact, since an unlocked box can only make
them slower. That plateau needs re-taking under $HOME/gpu.lock before it is
quoted as the kernel's achievable bandwidth. The two-regime SHAPE (L2-resident
under ~18 blocks, streaming above ~27) and the distinct-experts scaling are
robust to a uniform slowdown and do not need re-taking.

Both harness arms now carry the rule in their header, including that nvidia-smi
showing no compute apps does NOT mean the GPU is unreserved -- a holder may be
between phases -- so `fuser -v $HOME/gpu.lock` is the check.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
@localai-bot
localai-bot force-pushed the row/SPEC-DSPARK-MARLIN-NCU branch from 5ebbe53 to 69e9e7a Compare August 13, 2026 02:43
mudler added 4 commits August 13, 2026 06:21
…the bound direction was inverted (#442)

A fresh scoped review returned FAIL on this branch with two blocking findings.
Both are real, both are repaired here, and neither needed the GPU -- which is
just as well, since dgx.casa is down.

1. "Cost per DISTINCT EXPERT is flat at 5.2-5.7 us" is FALSE. Recomputing
   us/call / distinct from the section's OWN table gives 4.470, 5.678, 5.340,
   5.191, 7.495, 5.500, 5.194 -- a 4.47 to 7.50 range. The two distinct=20 rows
   are the tell: identical expert counts, 89.4 vs 149.9 us, blocks 73 vs 137.
   There, time tracks BLOCKS -- precisely what a flat-per-expert model calls
   irrelevant.

   The honest model has the same two regimes as the L2 finding one section
   earlier. Weights fitting L2 (low distinct) are not re-streamed, so cost is
   per-block work and time tracks BLOCKS. Weights not fitting (distinct >= ~40
   here) make cost streaming-bound, so time tracks DISTINCT EXPERTS at 5.2-5.7
   us each. time ~= distinct x 1.125 MiB / ~215 GB/s holds ONLY in the second
   regime: at distinct=20 it predicts 109.7 us against 89.4 and 149.9 measured,
   off by -19% and +37%.

   So the rule this spec promulgated -- "control distinct experts per launch" --
   was INCOMPLETE. Control BOTH distinct experts and blocks, or state the regime.

2. The lock caveat reached the record and STATUS but NOT the spec, which is the
   file a fresh implementer is told to read, and it gave the bound the WRONG
   DIRECTION. Contention inflates TIME, so a bandwidth computed as bytes/time is
   a LOWER bound on what the kernel achieves, not an upper one. A re-take under
   $HOME/gpu.lock can only move the plateau UP, which would put our in-situ
   209.9 GB/s BELOW it and REVERSE 6ag's inversion. "We are not slow, upstream is
   unusually fast" is the least favourable reading to us that the data admits and
   the one most likely to change on a clean re-take; it is now marked provisional
   in the spec itself.

Also repaired: the plateau range mixed bands (over the rows actually flat it is
212.7-225.8 GB/s, and the 203 endpoint is the 27-block row the same paragraph
excludes -- under the self-consistent range "ours sits INSIDE it" is FALSE); "at
matched blocks" overstated the harness, whose two arms draw routing from
independent RNG streams and therefore occupy different block counts at the same
pool, so blocks are NORMALISED rather than matched and neither arm can yet take
an external routing tensor; the blocks_per_sm entry credited the 8.7% to the
seeded pass when it came from the unseeded one; and --iters defaults differed
between the arms (python 200, C++ 80) while the prose asserted 80 for both.

benchmarks/marlin_moe_standalone.cpp is not wired into any build target, so
nothing compiles it and it will rot against vt::MoeGroupedGemmNvfp4Marlin's
signature. Recorded as owed in the file header and the record rather than
quietly left.

WHAT SURVIVES: 6ae's interleaved 0.9973 -- the kernel is not the gap --
because interleaving is exactly what protects a ratio against contention AND
routing draw. What does not survive as stated is 6ag's inversion.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
Brings in #583, which repairs the Invoke-Checked empty-argument defect that was
red on every open PR including this one, plus the Nemotron and intake records.
No overlap with this branch, which touches the DSpark spec, the benchmark
record, STATUS, BENCHMARKS and two standalone harnesses.

Worth noting for this row specifically: BENCH-ASSERT-CLOCK-STATE (51ec6be)
records that the SM clock moves 12.8% between boots. That is the same class of
effect this branch records as "absolute numbers move up to 5% between sessions
for the same binary", and it independently reinforces why only WITHIN-session,
interleaved ratios are quotable here.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…not 0.966 (#442)

Every ratio this row has recorded was taken with a COLD leading arm. Correcting
that moves the headline by two thirds of the deficit.

Four controls, together for the first time:

  * $HOME/gpu.lock. The standalone runs of 6ad-6af took /tmp/gpu.lock, which
    coordinates with nothing.
  * A DISCARDED warm-up arm ahead of the first measured arm. The GB10 SM clock
    ramps over MINUTES -- 1449 to 2190 MHz observed across one run -- so
    dropping rep 1 is not enough and an entire first arm reads ~6% low.
  * Settle barriers between arms. vLLM asserts free GPU memory does not GROW
    during its startup profile, and GB10 returns our engine's pages lazily, so
    an oracle launched straight after our arm dies with "Initial free memory
    68.53 GiB, current free memory 89.42 GiB". That killed every earlier paired
    attempt today.
  * A host-RAM headroom guard before the oracle. gpu_memory_utilization
    reserves HOST RAM here, so an oracle without headroom takes the MACHINE
    down: three reboots on 2026-08-13 (08:57, 09:29, 16:29), at least the last
    of them mine.

  arm            n    median tok/s   range
  ours BEFORE    9    142.604        141.67-142.79
  ours AFTER     9    142.140        135.23-142.87
  ours combined  18   142.534
  oracle         15   144.130        141.86-151.84

Drift before -> after is -0.33%, inside the 1% gate this harness sets for
itself, so the run counts. Ratios: 0.9894 before, 0.9862 after, 0.9889 combined.

THE GAP IS 1.1%, NOT 3.4%. The 0.9757 / 0.9646 / 0.9569 of 6ac were measuring an
unwarmed first arm as much as the engine.

This is NOT parity. 0.9889 is below 1.0 and the row stays open. What it changes
is the size of what remains -- and it lands after 6ae had already cleared the
kernel the deficit was attributed to, so the two findings agree: there was less
to explain than the record claimed, and it was not in Marlin.

Caveats kept rather than buried. n=1 paired run; a repeat is running and is owed
before this is settled. The ours-AFTER arm carries four low outliers (135.2,
135.5, 139.0, 139.9) the BEFORE arm does not, so something touched the box
during it -- medians unaffected, that arm's spread is not trustworthy. The
oracle shows ONE fast draw (151.84) against fourteen near 144, the old
bimodality appearing once, absorbed by the median. The oracle copy runs
gpu_memory_utilization=0.35 rather than 0.55; at max_num_seqs=2 and
max_model_len=2048 the KV cache needed is a fraction of either budget, so it
cannot change decode speed, but it IS a config delta on the denominator and is
recorded as one.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
One conflict, in .agents/benchmark-record.md, which is the append-only log --
union-merged, both sides kept in order. Verified rather than assumed: all 322
`## ` entries from main are present in the result, and this branch's new entry
survives alongside them.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants