Decode MXFP4 (fmt=7) on CUDA, so Kimi K3 has a CUDA expert tier - #819
Open
ZacharyZcR wants to merge 2 commits into
Open
Decode MXFP4 (fmt=7) on CUDA, so Kimi K3 has a CUDA expert tier#819ZacharyZcR wants to merge 2 commits into
ZacharyZcR wants to merge 2 commits into
Conversation
Kimi K3's routed experts are QAT in MXFP4 and streamed un-re-encoded, so
fmt=7 is the format its expert tier runs on. Only the Vulkan shader could
decode it: the CUDA backend understood fmt 0/1/2/3/4/6 and nothing else, and
c/Makefile said so outright --
@echo "*** kimi_k3 has no CUDA backend (JustVugg#783): building without it."
@echo "*** On NVIDIA, Kimi K3 runs on the Vulkan path"
-- while forcing NOCUDA_CFLAGS to strip -DCOLI_CUDA back out. An NVIDIA host
either went through Vulkan or ran the experts on CPU.
This adds the fmt=7 branch to quant_matmul plus a stateless entry point, and
wires kimi_k3.c's expert_apply to try it first under K3_CUDA=1. Decode only:
at S>1 the CPU kernels amortise over the batch and a per-call upload would
not pay. It returns 0 with the output untouched on any failure, so the caller
falls through to disk+CPU exactly as it does when Vulkan declines -- the same
try-then-fall-back contract vLLM's MXFP4 backends use (FlashInfer/AITER when
available, an emulation path when not).
Two decisions the reference implementation in quant.h dictated:
The ue8m0 exponent is decoded as a bit pattern, (uint32)s << 23 read as
float, not exp2f. That IS 2^(s-127) for s in [1,254] and reproduces the
CPU path's documented edges exactly -- s=0 gives +0, s=255 gives +inf.
exp2f would agree across the normal range and diverge at precisely the two
values where a silent mismatch would hide.
e2m1 is computed arithmetically rather than read from a __constant__ table:
a file-scope __constant__ array with static linkage is initialised per
translation unit, and this file is also compiled into the HIP build and the
Windows DLL.
VERIFIED ON HARDWARE, not just compiled. tests/test_mxfp4_cuda.cu diffs the
kernel against quant.h's matmul_mxfp4 -- the CPU path the engine already
trusts -- on an RTX 4070 (sm_89):
ok all 16 e2m1 codes decode exactly (cpu == gpu == spec)
ok decode + matmul S=1 I=64 O=32 worst rel 2.43e-06
ok multi-row batch S=4 I=128 O=64 worst rel 1.59e-05
ok wide rows (many groups) S=1 I=2048 O=16 worst rel 8.41e-07
ok non-multiple-of-32 columns S=1 I=96 O=8 worst rel 1.23e-07
ok tail group (I%32 != 0) S=1 I=80 O=8 worst rel 6.40e-06
ok exponent 0 -> +0 / 255 -> inf / 127 -> unit scale
The test earned its place immediately: the first run came back with results
off by 1e38 and it took one look to see why. quant_matmul ends with
y[...] = (fmt && fmt != 4 && fmt != 6) ? partial[0] * scales[o] : partial[0];
fmt=7 was not on that exemption list, so the per-group result was multiplied
again by `scales[o]` -- and for MXFP4 that pointer is ue8m0 BYTES, so it did
not merely double-scale, it read garbage as float. A compile check would have
passed. Only a differential run against the CPU could have caught it.
Tolerance is 1e-4 relative, not bit-exactness: the two accumulate in a
different order (CPU serial over columns, CUDA strided across threads then
reduced). Decode errors are not subtle at that scale -- the bug above moved
results by 30+ orders of magnitude.
make test-c passes; all four engines build; kimi_k3.c compiles both with and
without -DCOLI_CUDA.
NOT covered: end-to-end Kimi K3 on CUDA. That needs the 1.6 TB checkpoint,
which a 12 GB card cannot hold. What is proven here is that the kernel
decodes MXFP4 correctly on real hardware; throughput on a real model still
wants a machine that can load one.
tests/test_makefile_cuda_scope.py exists to stop CUDA=1 decorating an engine that has no CUDA backend: the define matches nothing, the cudart link is never called, and with no warning printed the compile line, the libraries and the exit status all say "CUDA build" while the GPU sits idle (JustVugg#783). It caught the previous commit immediately and correctly -- kimi_k3 now HAS a CUDA path, so three assertions written around "it does not" had to go. Good test. Rewriting it turned up that the guard was only ever half applied. olmoe.c contains zero COLI_CUDA references, and `make olmoe CUDA=1` was emitting: gcc ... -DCOLI_CUDA olmoe.c -o olmoe ... -lcudart -lstdc++ Exactly the failure JustVugg#783 describes, on the engine nobody checked. olmoe now builds through NOCUDA_CFLAGS/NOCUDA_LDFLAGS, which is where kimi_k3 used to be and no longer needs to be. The file now tests the rule from both sides: an engine WITHOUT the backend must not receive the flag (olmoe), and an engine WITH it must (kimi_k3, colibri) -- because silently dropping -DCOLI_CUDA would compile the new MXFP4 expert path out without a word, which is the same class of bug in the other direction. 289 Python tests pass; olmoe still builds.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The gap
Kimi K3's routed experts are QAT in MXFP4 and streamed un-re-encoded, so
fmt=7is the format its expert tier runs on. Only the Vulkan shader could decode it β the CUDA backend understoodfmt 0/1/2/3/4/6and nothing else.c/Makefilesaid so outright:β¦while forcing
NOCUDA_CFLAGSto strip-DCOLI_CUDAback out. An NVIDIA host either went through Vulkan or ran the experts on CPU.What this adds
fmt=7inquant_matmulplus a stateless entry point (coli_cuda_matmul_mxfp4), andkimi_k3.c'sexpert_applytries it first underK3_CUDA=1.Decode only β at
S>1the CPU kernels amortise over the batch and a per-call upload would not pay. It returns 0 with the output untouched on any failure, so the caller falls through to disk+CPU exactly as it does when Vulkan declines. That is the same try-then-fall-back contract vLLM's MXFP4 backends use (FlashInfer/AITER when available, an emulation path when not).Two decisions the CPU reference dictated
Exponent as a bit pattern, not
exp2f.(uint32)s << 23read as float is2^(s-127)fors β [1,254], and it reproduces the CPU path's documented edges exactly βs=0β+0,s=255β+inf.exp2fwould agree across the normal range and diverge at precisely the two values where a silent mismatch would hide.e2m1 computed arithmetically, not from a
__constant__table. A file-scope__constant__array with static linkage is initialised per translation unit, and this file is also compiled into the HIP build and the Windows DLL.Verified on hardware, not just compiled
tests/test_mxfp4_cuda.cudiffs the kernel againstquant.h'smatmul_mxfp4β the CPU path the engine already trusts. On an RTX 4070 (sm_89):The test earned its place on the first run. Results came back off by 1e38. One look showed why β
quant_matmulends with:fmt=7was not on that exemption list, so the per-group result got multiplied again byscales[o]β and for MXFP4 that pointer is ue8m0 bytes, so it did not merely double-scale, it read garbage as float. A compile check passes this. Only a differential run against the CPU catches it.Tolerance is 1e-4 relative, not bit-exactness: the two accumulate in a different order (CPU serial over columns, CUDA strided across threads then reduced). Decode errors are not subtle at that scale β the bug above moved results by 30+ orders of magnitude.
Also in scope
make test-cpasses, all four engines build,kimi_k3.ccompiles both with and without-DCOLI_CUDA, and the stale#783message plus theNOCUDA_CFLAGSoverride are gone from thekimi_k3rule.Not covered
End-to-end Kimi K3 on CUDA. That needs the 1.6 TB checkpoint, which a 12 GB card cannot hold. What is proven here is that the kernel decodes MXFP4 correctly on real hardware; throughput on a real model still wants a machine that can load one.
Also unmeasured: whether the per-call upload is worth it at decode on a real expert tier. The knob is opt-in (
K3_CUDA=1, default off) precisely because that trade has not been measured yet.