Skip to content

GLM-5.3 Flash performance campaign: 3.21x FP8, 4.96x MXFP4 warm short decode, plus MXFP4 release support - #42

Merged
ro99 merged 39 commits into
mainfrom
perf/brave-meadow-6c9d
Sep 2, 2026
Merged

GLM-5.3 Flash performance campaign: 3.21x FP8, 4.96x MXFP4 warm short decode, plus MXFP4 release support#42
ro99 merged 39 commits into
mainfrom
perf/brave-meadow-6c9d

Conversation

@ro99

@ro99 ro99 commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Campaign branch for GLM-5.3 Flash text performance. 38 commits; every mechanism
is measured, and every rejection is preserved with its evidence rather than
quietly dropped.

Headline, measured on the composed stack

Canonical protected short decode — 36-token prompt, 129 generated tokens,
context 2,048, two RTX 3090s, greedy, seed 33,377,335, numactl --interleave=all,
three fresh processes per release after a discarded warm-up:

release tok/s arms vs M0 baseline
MXFP4 5.490 5.48 / 5.49 / 5.49 4.96x
FP8 3.550 3.54 / 3.55 / 3.56 3.21x

M0's canonical warm short decode was 1.107 tok/s on FP8. These are integrated
figures, not a product of per-record deltas.

Long context (1,536-token prompt) went 1.423 → 4.132 tok/s from the MLA kernel
repair alone; it has not been re-measured since the static expert tier landed,
so no combined long-context figure is claimed.

What landed

  • MXFP4 release support. GLM-5.3-Flash now runs from either the FP8 E4M3
    block-128 checkpoint or the quark MXFP4 export, selected at open from pinned
    index extents. Host MoE gained MXFP4 and BF16 decoders beside the FP8 one.
    The E2M1 nibble order was measured, not assumed — 0.993 cosine against the FP8
    quantization of the same tensor versus 0.000002 for the swap.
  • MLA kernel geometry repair. glm53_mla_scores_kernel was launching 64
    blocks on an 82-SM GPU at 2.08% achieved occupancy and 0.64% of DRAM
    bandwidth. Long decode 1.423 → 4.132 tok/s, MLA kernel service −96.7%, exact.
  • A CUDA kernel-accounting fix. kernel_nanoseconds was accumulated in one
    file only, so the resident MLA, KDA and expert kernels reported ~zero. Every
    "the GPU is idle" conclusion in this campaign rested on that; it is repaired.
  • Static routed-expert residency, default-on for both releases: −20.1%
    MXFP4 / −19.1% FP8 decode at 35–40% measured route coverage.
  • Device shared-expert tier restored (FP8 −6.9%) and extended to BF16 for
    MXFP4 (−25.8% decode).
  • Resident MLA chain, page-major prefill with a hardware-independent KDA
    recurrence, exact concurrent decode cohorts, bounded admission derived from
    host and per-sequence CUDA state.
  • MXFP4 decoded with vpermps rather than vgatherdps: 1.87x the FP8 row dot,
    bit-identical.

What was rejected, with evidence

  • DFlash 2 speculative decoding. Sound mechanism, validated against merged
    llama.cpp PR #27342; worth 1.085x here against a 25–30% bar. Speculation
    amortizes fixed per-step cost, and after this campaign's repairs the step is
    72.7% expert weight streaming at the hardware ceiling. The MLA repair moved
    break-even from 2.32 to 4.58 — the largest win made the speculative milestone
    unreachable.
  • M7 aggregate throughput. 1.041x at concurrency 4. Cohorts form and host
    expert bytes/token fall 17%, but expert-major sharing converts memory traffic
    into arithmetic this host has none spare of. M7's paging, admission, prefix
    reuse and cancellation are retained on their own merits.
  • Rank-local TP2 and the anonymous huge-page expert arena, both measured and
    reverted; no residue in the tree.

The transferable result: every "reuse a weight read across more rows"
mechanism — speculation, wider pages, larger cohorts — is bounded by a ~55 GB/s
compute roofline against a 33 GB/s memory ceiling, already ~90% consumed at one
row per traversal.

Verification

Clean CPU and CUDA builds; 358/396 tests with 38 fixture skips; make check 4/4
with a Gemma fixture-only skip. No rejected mechanism remains in the tree. No
WIP or fixup commits. README.md and docs/models/glm53.md updated.

Known gaps

  • The full M8 matrix — cold load, warm load, first token, the three protected
    prefill shapes, the server concurrency/prefix matrix — has not been re-run
    on the composed stack. Only decode has.
  • The 8 tok/s stretch objective is not reached, and the measured ceiling
    argument for why is recorded rather than argued around.
  • Long-context decode has not been re-measured since static expert residency
    landed.

ro99 and others added 30 commits August 29, 2026 14:07
Adds the observability M0 needs to instantiate the campaign cost model, and
repairs the pre-existing race that the first protected prefill arm exposed.

Observability, opt-in and off by default:

- STRATA_GLM53_PHASE_PROFILE=1 emits structured [glm53-phase-profile] JSON
  covering per-phase graph time, attention/KDA/MLA/feed-forward, host-expert
  subphases, useful expert weight bytes, cache, CUDA transfer/call/sync,
  allocation, RSS, VRAM and token IDs. Default execution enables no CUDA
  event timing.
- STRATA_GLM53_PREFILL_PAGE_TOKENS controls scheduler prefill width for the
  M0 reuse sweep. The production default remains 64.

Every counter update is behind `if (config.phase_profile)`. The only work
left on the disabled path is the steady_clock reads that bracket each phase:
348 per decode step at a measured 18.665 ns, or 0.00072% of a 903 ms step.
Profiled and unprofiled arms produce identical token IDs.

KDA recurrent-state repair:

Head workers each called sequence.recurrent(layer) for the first time
concurrently, racing that span's lazy copy-on-write allocation and corrupting
the heap; the observable was exit 139 on a wide prefill page. The span is now
resolved and shape-checked once on the scheduler thread and workers take
disjoint head subspans of it. This changes neither arithmetic nor operation
order, and it strictly removes per-head work from the always-on path.

The two changes ship together because they are one unit of work: the paging
control is what exposed the race, and every arm in experiment 0196 requires
the repair to run at all.

Gates: make check passes; the custom suite is 331/369 passed, 38 skipped,
0 failed, unchanged from before this work. Full evidence, including a
discovered pre-existing defect where prefill page width changes generated
output, is in experiments/docs/experiments/0196.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
Campaign contract section 8 requires a load report carrying cold load, warm
load, first token, bytes read, faults, RSS, VRAM and any declared
I/O-dependent tier. This adds it, model-neutral, because nothing in it is
GLM-specific.

Layering: the /proc and /sys readers go in strata_platform, which already
reads sysfs for HardwareProfile and cannot depend upward; assembling a report
needs the checkpoint's backing device from resolve_backing_storage, which
lives in strata_engine, so the report itself goes there. RuntimeSession wires
it because load and first token are model-neutral boundaries it already owns.

The report carries one field the contract does not name and experiment 0197
showed was the missing measurement: average storage queue depth per phase,
from the kernel's weighted in-flight time -- the figure iostat calls aqu-sz.
0197 could not distinguish "the runtime issues one read at a time" from "the
device cannot go faster" from outside the process, and rejected three
candidate mechanisms without finding the real one.

Its first run settles that question and reframes it:

  load         1.14 s   read  0.00 GB          0 major faults   rss   0.45 GB
    device queue depth 0.01, busy  0.0%,        0 reads
  first-token 71.88 s   read 18.90 GB  1,340,638 major faults   rss 136.93 GB
    device queue depth 4.79, busy 37.9%, 1,514,102 reads of 12.5 KiB

Queue depth 4.79 means the runtime is not serialized, so adding threads or
issuing reads from more places cannot help. What it is doing is assembling
18.90 GB out of 12.5 KiB requests while the device sits 62% idle. Every probe
in 0197 read contiguous 64 KiB to 4 MiB chunks, which is why none reproduced
the production rate.

The report also measures, in-process, the reframing 0197 argued from arms:
`load` is 1.14 s and reads nothing, while `first-token` carries every byte and
every fault. On a lazily mapped checkpoint there is no load time to optimize.

Marks are taken at boundaries only -- two /proc reads and two sysfs reads per
phase -- so nothing samples inside a decode step. Emission is gated on the
existing report_placement_plan switch.

Gates: make check passes; suite 338/376 passed, 38 skipped, 0 failed, with
seven new tests covering queue-depth derivation, the serialized-reader shape,
absent and non-monotonic counters, zero-length intervals, and render before
any phase is marked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
Closes three of the four discovery gaps M1's audit found. Campaign contract
section 8 requires discovering huge-page availability, PCIe locality and the
peer/P2P/NVLink matrix; none of the three was recorded anywhere.

Huge pages, into HardwareProfile: page size, explicit hugetlbfs total and
free, and whether transparent huge pages are enabled at all and in which mode.
The reference host reports 2 MiB pages, zero explicit pages reserved, and THP
in `madvise` rather than `always` -- so a long-lived arena would have to ask
with MADV_HUGEPAGE, and nothing does. That is now a visible fact.

PCIe locality and peer ranking, into the placement plan. CudaBackend already
exposed device_numa_node and high_speed_peer_access_supported and both were
queried at runtime, but nothing persisted them, so a cached plan could not be
verified against the topology it was built for. The plan now carries per-device
NUMA node and a row-major high-speed peer matrix, and the report prints them:

  devices  cuda:1 (23.30 GiB free of 23.56 GiB, numa 1), cuda:2 (..., numa 1)
  peer     no high-speed peer link between admitted devices

Both match what M0 recorded by hand in the frozen manifest, which is the point:
discovered rather than asserted.

Schema is bumped to v3 and v2 plans are discarded rather than upgraded. A v2
plan carries no peer matrix, and reading that as "no high-speed link anywhere"
would let a cached plan refuse a fast path the machine actually has -- the
failure being avoided is silent and one-directional, so the version gate is
the conservative choice.

Two encoding details worth naming. numa_node is -1 when the driver declines to
say and JsonCursor parses unsigned only, so it is stored plus one rather than
widening the JSON layer for a single field. The peer matrix is uint8_t, which
streams as a character, so it is written through an explicit unsigned cast; a
test covers the round trip of both, including the -1 sentinel and the clear
diagonal.

Still open, deliberately: the pinned host-memory budget. No cudaHostAlloc or
cudaHostRegister exists in the runtime, so every transfer is pageable. It is
immaterial at short context but M0 measured 295.77 GB of D2H at long context
on the MLA fallback path, and the transfer path it would serve is M4's to
change.

Gates: make check passes; suite 340/378 passed, 38 skipped, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
…ol width

Three M1 gate items, all small.

Admission now refuses a plan whose peer topology differs from the probed
machine. Capacity mismatches already failed loudly; a peer mismatch would not
have. A plan cached on a peer-linked host and replayed on one without the link
would admit a fast path the hardware cannot serve, and the run would simply be
slower and wrongly attributed rather than rejected.

Placement determinism is now gated by a test rather than assumed: two solves of
identical checkpoint, topology and request encode byte for byte, and a plan
survives an encode/decode cycle unchanged. Comparing encodings covers the
memory accounting as well as the layer assignment, since any field that drifted
between two solves would change the encoding.

The DeepSeek host MoE pool takes its width from HardwareProfile instead of a
hard-coded 48, which was that executor's development box's logical CPU count
asserted as a constant -- the class of fact hardware_profile.hpp exists to stop
keeping in the type system, and a silent mis-size on every other machine. The
policy stays in the executor (one worker per usable CPU across every node,
because a both-shards pool spans them); only the measurement moves. This is
safe rather than merely plausible: the sole production construction, in
deepseek_rank_local_layer_executor.cu, passes both_shards=false with an explicit
CPU list, so the constant was never reached on any live path. The accompanying
test pins both shapes.

Gates: make check passes; suite 343/381 passed, 38 skipped, 0 failed. Decode and
prefill were measured back to back against a rebuilt f5b203e under identical
host conditions -- decode -1.09%, prefill +0.57%, output byte-identical -- which
is M1's 2% clause. See experiment 0199, including why comparing against a
recorded figure could not have shown that: MLA wall time swings 2.37x with host
state on unchanged code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
… figure

The load report printed 2.15 GB of VRAM per device where the runtime actually
used 19.65 GB, a factor of nine. It had been handed plan.device_resident_bytes,
which is the workspace the plan reserves up front, not the device expert cache
the runtime demand-fills.

Found by M1's own "no unaccounted full-size duplicate representation" audit,
which is the one place that error would do real damage: a report understating
VRAM by 9x invites exactly the conclusion the audit exists to test.

It now samples the driver at every phase mark. Devices are set before the first
mark so load and first-token both carry a figure, and the difference between
them is informative in itself:

  load        1.19 s   vram 19.41 GB in use, 19.41 GB in use
  first-token 39.14 s  vram 19.62 GB in use, 19.63 GB in use

The device is already at 19.41 GB when load returns, so the expert cache is
filled by the background warm-up thread rather than during the first forward --
an M1 loading fact the report shows only because the figure is measured rather
than planned.

An unreadable device contributes zero rather than aborting the report; the
storage, fault and RSS figures are still worth having.

Gates: make check passes; suite 343/381 passed, 38 skipped, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
…rvice

M2's first mechanism, and it is a scheduling change rather than a layout one.

HostWorkerPool::parallel_for hands out consecutive indices from a single
counter, and the GLM host expert loops index tasks as
(expert * intermediate + row). Twenty-eight runners therefore walked one
contiguous FP8 matrix with a 28-row stride each, so the hardware saw 28
interleaved strided streams where the source reads as sequential. Experiment
0198 measured that shape at 0.69 GB/s against 1.96 GB/s for contiguous blocks
on identical cold bytes; this is the warm answer.

parallel_for_blocked claims `block` consecutive indices per fetch_add.
parallel_for now delegates to it with block 1, so no other caller changes. The
four GLM expert loops use 64: 256 KiB of contiguous FP8 weight per claim, wide
enough for the prefetcher, narrow enough that 28 runners still balance across a
2048-row expert at 32 claims each.

Measured on protected short warm decode, one binary, three arms back to back,
bracketed by repeats so host drift is bounded rather than assumed:

  block 1   96.75 s decode   72.70 s expert service   16.75 GB/s
  block 64  78.76 s decode   50.03 s expert service   24.34 GB/s
  block 1   97.39 s decode   72.94 s expert service   16.70 GB/s

Host expert service -31.2%, useful bandwidth 1.45x, decode 1.323 -> 1.625 tok/s,
prefill 34.60 -> 13.53 s. Host drift between the bracketing arms is +0.32%, so
the effect is about a hundred times the drift. The down projection carries it at
-56.1%: it reads 4,096 rows of 2,048 bytes, so a 28-way stride breaks a shorter
run than gate/up's 4,096-byte rows.

Exactness is measured, not argued. Each task writes a disjoint output element,
so reordering changes no arithmetic; useful expert bytes are identical to the
byte across arms (1,217,920,499,712) and output matches M0's canonical hash.

STRATA_GLM53_EXPERT_DISPATCH_BLOCK exists so both arms of the A/B run the same
binary; 1 reproduces the previous dispatch. Production default is 64.

Against M2's >=40 GB/s gate this reaches 24.34 from 16.75. It does not clear the
gate alone and was not expected to; 30,208 timed allocations per 128 decode
tokens and timed view resolution remain, both already counted by the profiler.

Gates: make check passes; suite 346/384 passed, 38 skipped, 0 failed, with three
new tests covering coverage, contiguity within a claim, and the zero-block guard.
See experiment 0200.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
M2 requires timed allocations and checkpoint view resolution to be zero. M0
counted both every step: 30,208 timed temporary expert allocations per 128
decode tokens, and view resolution at 0.35-0.72 s.

Expert views are now resolved once into a process-lifetime cache indexed by
(layer, expert slot), slot 288 being the shared expert. Every MoE call had been
rebuilding three module-name strings per expert and performing three manifest
lookups -- 27 per call, 5,376 calls per 128 decode tokens.
Glm53CheckpointReader::view computes a span into the mapping and touches no
pages (read is the one that moves bytes), so caching a view changes nothing
about what is read, only how often the manifest is consulted. The cache is
mutex-guarded rather than lock-free and the lock is taken once per call around
the whole group: the call site is the scheduler thread today, but nothing in the
type system says so.

The MoE scratch buffers are grown once and reused. Every element is overwritten
each call -- gate/up writes every activation slot, down every output slot -- so
reuse without clearing is exact, and the byte-identical output gate verifies
that rather than the argument doing so. The allocation counter now reports what
happened instead of a constant, so the profiler shows the term reaching zero.

The first attempt used function-local `static thread_local` scratch and
segfaulted: a thread_local resolves to the *worker's* copy inside the dispatch
lambda, not the caller's, so all 28 workers indexed into their own empty
vectors. The buffers are filled by the worker pool, so they are Impl members,
for the same reason host_moe_workers is.

Measured three arms back to back against 140781e, bracketed for drift:

  prev  49.11 s expert service  24.80 GB/s  30,208 allocs  440.6 ms view
  curr  48.19 s expert service  25.27 GB/s       3 allocs  130.2 ms view
  prev  49.60 s expert service  24.56 GB/s  30,208 allocs  518.3 ms view

Timed allocations 30,208 -> 3, and zero in prefill. View resolution 3.4x
cheaper. Expert service improves 1.88% against 0.98% host drift, which is about
twice the drift and is reported as exactly that -- a small real effect near the
noise floor, not a throughput win. The removed terms were 0.44 s of view time
and some allocator work inside a 49 s step, so a few percent was the arithmetic
ceiling before the arms ran. Prefill shows no detectable change. Output is
byte-identical across all three arms.

Useful expert bandwidth stands at 25.27 GB/s against M2's >=40 GB/s gate. The
remaining mechanism is the one the gate was written for: a transformed, tiled,
NUMA-local expert representation.

Gates: make check passes; suite 346/384 passed, 38 skipped, 0 failed. See
experiment 0201.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
M3's first lever: run the ninth expert of every MoE layer -- the shared
one, the only one the router does not choose -- on the GPU that owns the
layer, while the host works through the other eight. M0 attributed 67.7%
of a decode step to host expert service with both GPUs idle for 94% of
it, so this screened at 7.5% of the step.

The obstacle was exactness, not capacity. The existing FP8 MoE kernels
sum with a block reduction while glm53_host_fp8_dot_avx2 sums into eight
__m256 accumulators and combines them with a fixed tree, and floating-
point addition is not associative. glm53_shared_expert_dot_kernel
replicates the association: one thread per output row holding an 8x8
register block that is the AVX2 register file, the same
fma(mul(decoded, scale), activation, acc), and the same combine tree.
Every rounding and the SwiGLU stay on the host, which keeps expf -- where
glibc and CUDA differ by up to 2 ULP -- out of the comparison entirely.

It is bit-exact: zero mismatches over 8,192 output rows at sm_86 and at
the production compute_75 JIT, and 129-token decode is byte-identical
across five alternating arms at the hash M2 has produced since 0200.

It does not pay, so it ships off. Removing 8.20% of the expert bytes the
host services took 3.7% off the gate/up dispatch and 2.5% off down, then
gave 1.0 s back waiting on the device collect: net -0.84% on host expert
service against host arms whose own spread was 0.48%, and device arms
with ten times that variance. At full coverage that is about 1.4% of a
decode step, for 1.06 GB of the VRAM M4's routed hot tier wants.

The mechanism is retained default-off because M4's expert-residency work
needs exactly these primitives -- an exact device FP8 expert dot, a
checkpoint-native resident tier, and an arena reservation taken before
the weight cache is built with the capacity.

Two measurements that outlive this lever:

- Warm decode reads nothing from storage. Sampling /proc/<pid>/io across
  a full run records zero bytes and zero major faults during the decode
  window. The load report's "I/O dependent" verdict is a capacity
  projection, not a property of this operating point.
- STRATA_GLM53_EXPERT_BOUND prices an expert directly. Across the
  resolved range, removing 41.0% of host expert bytes bought 26.3% of
  host expert service and 57.4% bought 40.7% -- roughly 0.7% of service
  per 1% of bytes. That is the number M4's hot tier has to clear, and it
  is now measured rather than assumed. These arms produce incorrect text
  by construction and are never candidates.

Also fixes an inference that would have been wrong: [glm53-shared-expert]
reports which layers took which path, and it showed 11 of the 42 MoE
layers reaching the MoE through host_moe_page rather than host_moe even
at a single decode row -- the MLA fallback layers.

Records 0210, 0211.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
Section 11's first gate item needs sequential decode routes with reuse
distance and byte-weighted coverage before any residency policy is
designed, and there was no way to get them out of the runtime.

STRATA_GLM53_ROUTE_CENSUS=<path> writes one row per routed MoE layer
visit: phase, request, position, layer, and the eight experts the router
chose in order. 42 layers times 8 experts is 336 uint16 per decode
token, so a 128-token census is 86 KB and can be held whole and written
once rather than streamed.

Recorded before the prefetch guard in observe_route, because prefetch is
a separate opt-in that is off whenever the host expert path owns the
experts -- which is exactly the configuration whose routes M4 needs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
The kernel was exact but slow for a structural reason: one thread per
output row streams a whole 4,096-byte weight row alone, which is
entirely uncoalesced. It measured 9.8 GB/s on gate and up against a
3090's 936, and 2.149 ms per expert against the roughly 1.04 ms the
28-core host pool takes -- the GPU was twice as slow per expert as the
CPU it was meant to relieve.

The fix costs nothing in exactness because the association already had
the right shape. The host sums into eight __m256 accumulators; assigning
one thread per accumulator *group* rather than per row means thread g
owns accumulators[g] and all eight of its lanes, and reads
block[g*8 + lane] -- so the eight threads of a row read the 64 bytes of
a block contiguously, as one uint2 and two float4 each. The width-4/2/1
combine tree becomes eight __shfl_down_sync per step, and thread 0 ends
holding exactly the accumulators[0] the intrinsics would have held.

95.6 GB/s on gate and up, 159.0 on down: 9.8x and 8.1x, 0.229 ms per
expert. Zero mismatches against glm53_host_fp8_dot_avx2 over all 8,192
output rows, and 129-token decode is still byte-identical at
b3deffc5d0f0.

This matters for M4 rather than for the shared expert. The route census
(0212) puts a VRAM-feasible static tier at 28-37% of routed bytes, and
the projection assumed the GPU could serve its share inside the host's
window. At 2.149 ms per expert the device was 90% of the critical path
and the projection was about to collapse; at 0.229 ms it is 9%, and the
constraint moves back to VRAM where the census can reason about it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
Correction to the verdict recorded with the kernel two commits back. The
lever was measured at -0.84% and held off; that measurement was taken
against a device dot running one thread per output row, at 2.149 ms per
expert against the host pool's ~1.04 ms. The GPU was slower per expert
than the CPU it was relieving, so the collect wait ate the dispatch
saving. Coalescing the kernel (2193ee3) changed the input to that
decision, not just its margin.

Re-measured with the same five-arm alternating protocol: both device
arms beat the host arms bracketing them, by 4.49% and 6.33% of host
expert service. Mean -4.93% on service, -2.79% on decode wall. gate/up
-7.16% and down -5.98%, which is what removing one expert of nine
predicts from makespan alone -- 288 blocks over 28 workers falling from
eleven rounds to ten is -6.7%. The two device arms agree to 1.6% while
this session's host arms span 7.06%, so the reading rests on the
bracketing comparison rather than on means.

Output is byte-identical at b3deffc5d0f0 across all five arms, the hash
this campaign has produced since 0200.

The tier costs 1.06 GB, about one expert per layer of the K=11 that the
route census measures as VRAM-feasible, and returns several times that.
Automatic admission declines silently when the capacity is not there; an
explicit opt-in that cannot fit is an error.

Still open: 11 of the 42 MoE layers reach the MoE through host_moe_page
at a single decode row and do not take the device path. Covering them
should take -2.79% to roughly -3.8%.

Records 0210, 0211, 0212.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
… defect

Continues 0214 from where the previous session stopped. Its closure
engineering is done and its exactness ladder now has a verdict.

Workspace reservation. The rewritten resident path sized its workspace
from `history * expanded_width`, which grows by 16,384 floats per token,
so it would cudaFree and cudaMalloc inside a timed decode step -- which
0214's gate forbids outright and which would also make a step's cost
depend on when the buffer last grew. It now reserves
max(history, maximum_context), 134 MB at 2,048 tokens on this model,
inside the workspace reserve the planner already withholds. Offsets use
the reserved extent so a workspace pointer means the same thing at every
position.

Two real arithmetic mismatches against the host fallback, both fixed:
the score dot used a separately rounded __fmul_rn/__fadd_rn where the
host writes `score += q * kv` and GCC contracts it to an FMA on this
FMA3 host; and the scale used rsqrtf, a 2 ULP approximation, where the
host computes 1.0f / sqrtf once.

A second oracle on the latent cache itself, comparing the device state
buffer's row against the host cache's after the fallback appends it.

Verdict: the candidate fails and is not timed. Two-token smoke and the
4-token layer-boundary discriminator pass; 129-token repeated decode
state evolution diverges at token ~35. Localized to layer 15, position
45, one BF16 ULP. The latent oracle never fires, so the carried state is
bit-identical and the defect is in the published branch. Neither
arithmetic fix moved the failing values at all, which rules out the
score path.

The cause is expf. CUDA's differs from glibc's in 30.38% of f32 results
and 0.000375% after BF16 rounding; GLM-5.3 makes 64 heads x history x 11
layers exponential calls per token, so at history 46 a token has an
11.4% chance of a visible divergence and the nine steps from position 36
to 45 survive with probability 0.34. That predicts the observed failure
and explains why a four-token oracle passes.

This is not a kernel defect. Two independently implemented exponentials
disagree in the last bits and the exact-mode contract makes the host's
the definition. The fix follows the accepted shared-expert precedent:
the device does the linear algebra, the host does the transcendental.
Return the 64 x history scores for exp and normalization -- 12 KB per
layer at history 46 against the 13.4 MB per layer per token the chain
removes.

Record 0214.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
Confirms 0214's expf diagnosis by fixing it. CUDA's expf and glibc's
disagree in 30.38% of f32 results and 0.000375% after the BF16 rounding
the model applies to the coefficient; at 64 heads x history x 11 layers
that is an 11.4% chance of a visible divergence per token at history 46.

glm53_mla_decode_to_mhc now stops at the raw scores and returns
heads * history of them. The runtime applies the maximum, std::exp, the
sum and the BF16 coefficient rounding using the accepted fallback's own
arithmetic. glm53_mla_decode_finish uploads the coefficients and
completes the value-weighted sum and the output projection. The fused
attention kernel is split into glm53_mla_scores_kernel and
glm53_mla_weighted_kernel, both keeping the host's sequential
association and FMA contraction, and the workspace carries
heads * reserved_history extra floats for the exchange.

The round trip is 12 KB per layer at history 46 and 512 KB at the
2,048-token ceiling, against the 13.4 MB per layer per token of
activation D2H the resident chain exists to remove.

The 48-token layer-boundary oracle, which previously died at layer 15
position 45, now completes with no mismatch at any MLA layer or
position.

The 129-token gate still fails, and the two facts together locate what
is left: the oracle certifies every MLA layer's branch through 48
tokens, yet the text diverges at byte 184, inside that window. So the
remaining divergence is not in the MLA attention.

It is in the rest of the layer. forward_layer_resident is admitted by
(glm53_kda_layer(layer) || resident_mla_enabled()), so enabling resident
MLA moves those 11 layers' mHC pre, input layer norm and mHC post onto
the device too -- the control runs them on the host for exactly those
layers. The oracle downloads `normalized`, which is the output of the
device mHC and norm, and feeds it to the host attention, so it is blind
by construction to a difference in what produced it.

Candidate remains rolled back and untimed. Next is a direct host/device
comparison of mHC pre, the input norm and mHC post on those 11 layers.

Also corrects the reserved workspace figure: 268 MB at 2,048 tokens, not
134 -- expanded_width is 32,768 because it holds key and value together.

Record 0214.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
0214's attention oracle downloads the layer input that the device mHC
and norm produced and feeds it to the host attention, so it compares
attention given identical input and is blind to whatever produced that
input. This adds the comparison it could not make: download the same
layer input and check it against a host mhc_pre plus norm computed from
the same streams and the same two tensors the device was given.

It runs on every resident layer, not only the MLA ones, because the KDA
layers take the device path in both arms and a difference there would
mean the control is itself a mixture.

It is. The oracle fires at layer 0 -- a KDA layer -- at the first decode
position: host -0.003372 against device -0.004150, 7.8e-4 absolute. The
resident MLA work did not introduce this and nothing in this campaign
had compared the two implementations before.

A plausible mechanism is precision: the host mhc_pre accumulates the
stream RMS and all 24 projection rows in double before a 20-iteration
Sinkhorn split with a 1e-6 tolerance, and an f32 device projection would
feed slightly different values into an algorithm that amplifies them.
Recorded as an unresolved discrepancy rather than a device defect: it is
not ruled out that this host reconstruction is incomplete.

If it holds, M4's MLA exactness gate cannot be met as written. The
control runs device mHC on 34 layers and host mHC on 11, and the
campaign's reference hash encodes that mixture, so a candidate that
moves 11 layers between the two implementations cannot be byte-identical
to it however exact its attention is -- which is exactly what today
measured: the MLA branch is provably exact at every layer and position
through 48 tokens and the text still diverges.

Record 0214.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
Settles the mHC question and fixes the 129-token gate.

The two mHC implementations are different functions. The survey oracle
disagrees on essentially every element of every layer, KDA and MLA
alike -- 4096/4096 at layers 0 and 4, worst 0.16 absolute. Confirmed
without the oracle: STRATA_GLM53_FUSED_KDA=0, which puts all 45 layers
on the host path, yields bf2016cd3e39 against the production
b3deffc5d0f0, diverging at byte 391, with three repeats confirming the
production configuration is deterministic.

So the campaign's reference hash encodes a mixture: device mHC on the 34
KDA layers, host mHC on the 11 MLA fallback layers. Host mhc_pre
accumulates the stream RMS and all 24 projection rows in double before a
20-iteration Sinkhorn split; the device does the same work in f32 with
split partial reductions, and since collapsed is linear in the Sinkhorn's
pre weights a precision difference becomes a percent-level one.

That is why the gate could never pass. Enabling resident MLA moves 11
layers between the two implementations, so its output must change however
exact its attention is -- which is exactly why the layer oracle could
certify every MLA branch through 48 tokens while the text diverged.

dsv4_mhc_publish_branch and STRATA_GLM53_RESIDENT_MLA_HOST_ATTENTION give
the control the candidate actually needs: device mHC on all 45 layers in
both arms, differing only in where the MLA attention runs.

The gate passes. Three control and three candidate observations,
alternating, 129 tokens, all six arms one hash fe74dc4ec7ab:

  decode wall        68.75 -> 52.26 s   -24.0%   (control spread 3.3%,
                                                  candidate within 1.2%)
  MLA                18.60 ->  3.93 s   -78.9%
  activation D2H     18.99 ->  0.32 GB  -98.3%
  synchronizations   14,208 -> 7,168    -49.5%
  synchronization s   5.59 ->  0.22     -96.0%

This is the largest measured win in the campaign and it is exact.

It does not ship yet. fe74dc4ec7ab is a third configuration -- device mHC
on all 45 layers -- so landing it changes production output, not because
the attention is inexact but because it makes the model compute mHC one
way instead of two. Nothing in the architecture justifies the split and
the campaign had never compared them, so this is arguably a latent defect
being repaired; but it re-baselines every figure anchored to
b3deffc5d0f0. Recorded as an owner decision in the contract's blockers.

Record 0214.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
Owner ruled that the model computing mHC two different ways by layer
type is a latent defect being repaired, not a regression introduced, and
authorized the re-baseline. Resident MLA is now on by default and all 45
layers use one mHC.

Exactness rests on the isolated gate from the previous commit: with the
mHC held fixed on both arms and only the attention varying, three
control and three candidate observations are byte-identical.

Shipped before/after, one protected session, new/old/new:

  new  fe74dc4ec7ab  52.37 s
  old  b3deffc5d0f0  77.40 s
  new  fe74dc4ec7ab  52.57 s

Decode 77.40 -> 52.47 s median, -32.2%, 1.65 -> 2.44 tok/s, 1.48x. That
is larger than the isolated -24.0% because the 11 MLA layers also stop
paying host mHC.

The campaign reference hash moves from b3deffc5d0f0 to fe74dc4ec7ab.
Every figure anchored to the former describes the pre-repair mixture.

STRATA_GLM53_RESIDENT_MLA=0 restores the old path.
STRATA_GLM53_RESIDENT_MLA_HOST_ATTENTION=1 keeps the new mHC while
running the attention on the host, which is the control that closed the
gate and is retained for future exactness work.

Record 0214.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
GLM-5.3-Flash is published in two releases Strata can execute: the pinned
FP8 E4M3 block-128 checkpoint the campaign targets, and a quark MXFP4
export whose routed experts are E2M1 nibble pairs with one E8M0 scale byte
per 32 columns. Only the routed experts are quantized in either; the MXFP4
release leaves its whole spine, its shared experts, and the routed experts
of layers 3, 5 and 6 in BF16 under the publisher's mixed-precision
correction, where the FP8 release stores that spine E4M3.

The release is resolved once, at open, from pinned index extents, and every
layer below branches on the manifest rather than on a build flag or a flag
the caller has to remember. The host MoE gains an MXFP4 decoder and a BF16
decoder alongside the existing FP8 one, all three sharing the eight-chain
accumulator structure so the formats differ only in how a weight is decoded.
Sixty-four columns span exactly two group-32 scales, which is what keeps
that structure intact for MXFP4.

The FP8 path is untouched: same dot, same activation quantization, same
device shared-expert tier. Both of those are FP8-specific and are now gated
on the release, because MXFP4 is a weights-only format with no device
counterpart and quantizing its activations to E4M3 would discard precision
the format never asked to lose. An explicit shared-expert opt-in against an
MXFP4 checkpoint says so rather than silently running the host path.

The nibble order is measured, not assumed: dequantizing layer 20 expert 0
from both releases and correlating gives 0.993 for column 2b in the low
nibble of byte b and 0.000002 for the swap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
Both host expert dots run at 2.7-2.8 GB/s at the production gate/up shape,
which says the row dot is uop-bound, not byte-bound: halving the checkpoint
buys nothing by itself, and what separates the formats is the instruction
sequence that turns a stored code into a float.

`vgatherdps` is microcoded on this Broadwell host. The FP8 dot has to pay
it -- a 256-entry E4M3 table has no in-register form -- but E2M1 does not:
its sixteen values are eight magnitudes and a sign bit, so one
`vpermps` on an eight-float magnitude table plus a shifted XOR decodes
eight nibbles. The result is bit-identical to indexing the value table,
sign of zero included, and it is what the format's structure allows rather
than a numerics trade.

Measured single-threaded, 4096 columns, 2048 rows, three runs:

  fp8    1484-1529 ns/row
  mxfp4   782- 808 ns/row   1.87x
  bf16    930- 946 ns/row   1.60x

The gather form it replaces measured 2390 ns/row against FP8's 2529 under
the same load -- 1.06x, which is why the first integrated arm showed no
win and is worth recording as the reason.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
0213 turned this off: three device arms measured +1.71% decode while the
tier removed 11.1% of host expert bytes, and host expert service moved
only -0.21%. A saving worth about 5.4 s materialized as about 0.1 s and no
record said where it went.

It went into the round trip, and the round trip was expensive because the
device was busy. 0213 predates the resident MLA chain landed in 0214, which
took activation D2H from 2.265 to 0.002 GB per token, kernel time down
98.3% and synchronizations down 49.5%. The device is now idle at 0.77% of a
decode step and the round trip costs 70 us. The mechanism did not change;
its dependency did.

Re-measured as 3+3 alternating pairs with an ordering repeat, twice, since
a shipped default must not rest on a benchmark-only memory policy:

  interleaved     decode 50.130 -> 46.650 s  -6.94%, service -7.77%
  default policy  decode 50.360 -> 46.890 s  -6.89%, service -7.50%

4.1-4.3 s of host dispatch work removed against 0.76 s of collect wait
added. No range overlap in either pair, ordering repeats +/-0.20%, all
sixteen outputs hash fe74dc4ec7ab. The byte-to-service conversion is 0.70
per 1%, which is the curve 0211 measured.

The default and an explicit request are now distinct values, because they
must differ where a checkpoint cannot host the tier: the kernel decodes
E4M3, so an MXFP4 checkpoint declines silently under the default and errors
under an explicit opt-in rather than quietly running the host path that
opt-in asked to replace.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
Keep the release's BF16 shared-expert weights in checkpoint-native form and reproduce the host AVX2 eight-accumulator dot association exactly on CUDA. The host still owns every BF16 rounding, clamp, SwiGLU, and reduction boundary.\n\nThe format-tagged descriptor preserves the existing FP8 path while automatic admission now selects FP8 or BF16 from the opened checkpoint. The BF16 tier is 2.11 GB across all 42 MoE layers and reduces the cache arena by the same physical bytes before allocation.\n\nA row-complete CUDA test compares all three BF16 projections bit for bit against the host dot. The real MXFP4 two-arm smoke is output-identical and make check passes.
Document the two pinned checkpoint representations, manifest-driven runtime selection, exact native shared-expert tiers, current defaults, and the protected 3.88 tok/s MXFP4 operating point.
ro99 and others added 9 commits August 31, 2026 11:11
…context limit

The chat and server commands omitted two things the published decode rates
depend on. CUDA_DEVICE_ORDER=PCI_BUS_ID is required because many shells
export FASTEST_FIRST, under which --devices 1,2 silently selects a different
card and the run still succeeds with different output. numactl
--interleave=all is worth about 1.13x and removes a 6.7% run-to-run placement
lottery measured in 0218.

Also documents why context is capped at 2,048: the checkpoint's k-pool sparse
indexer is unimplemented, and the dense MLA workspace would need 47.2 GiB at
32,768 tokens against a 2 GiB per-device reserve. It is a missing feature, not
a knob, and it is refused at admission rather than silently approximated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jtzY5AZ3KBAjBA5BHgPzH
@ro99
ro99 merged commit 9254402 into main Sep 2, 2026
2 checks passed
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.

1 participant