Skip to content

[Cake 12/13] gated_act_mxfp8: fused SwiGLU + MXFP8 quantization, forward and backward - #13

Merged
Zhichenzzz merged 149 commits into
radixark:mainfrom
yyihuang:cake-stack/12-gated-act-mxfp8
Nov 5, 2026
Merged

Zhichenzzz merged 149 commits into
radixark:mainfrom
yyihuang:cake-stack/12-gated-act-mxfp8

Conversation

@yyihuang

@yyihuang yyihuang commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Stack 12/13 · base: main

Summary

Adds miles_ext_kernels.cake.gated_act_mxfp8, which fuses SwiGLU with MXFP8 quantization in the forward and in the backward. The output is E4M3 values plus E8M0 scales in the swizzled layout that MXFP8 GEMMs take as operands. The op comes from the FlashInfer package cake_gated_act_mxfp8_backend, vendored byte for byte from flashinfer-ai/flashinfer@c5db8d3. The module exports the package's two entry points with their signatures unchanged:

  • silu_and_mul_mxfp8_quantize(gated_input, *, rowwise=True, colwise=False) takes a BF16 [M, 2K] input (gate columns [0, K), up columns [K, 2K)) and quantizes silu(gate) * up, which is [M, K].
  • silu_and_mul_mxfp8_quantize_backward(gated_input, grad_output, *, rowwise=True, colwise=False) takes a BF16 grad_output [M, K] and quantizes the gate and up gradients, which are [M, 2K].

Both entry points return (row_output, col_output, row_scales, col_scales):

  • E4M3 values, with RCEIL E8M0 scales per 32 elements of a row and/or a column;
  • scales in the swizzled 128 × 4 layout of MXFP8 GEMM operands;
  • a zero-size pair for a disabled orientation. At least one of rowwise / colwise must be set.

The op has one variant, det. A call runs one program chosen by M * K, every program of a route writes the same bytes, and no call synchronizes the host. Calls therefore repeat bit for bit and can be captured in a CUDA graph. FlashInfer has no fast (non-deterministic) and no KCP package for this kernel. From flashinfer-ai/flashinfer#8023: "Cake has no other variants of this kernel beyond these six BF16-to-MXFP8 forms with the swizzled scale layout".

No miles argument selects the op (OP.selected_by = ()). It registers no flag and has no miles call site, so there is no paired miles PR.

Stack

Layer 12 of 13. This is a new PR; the op was not part of the earlier 11-layer stack. It builds on layer 11 (lm_head, #11), and layer 13 (mok, new PR) builds on this one.

Every PR in the stack targets main, so this diff also contains layers 0-11. This layer is these three commits:

Commit Subject Files Lines
e6129b5d vendor: gated_act_mxfp8 packages 132 +109,311
904c3bcc gated_act_mxfp8: fused SwiGLU + MXFP8 quantization, forward and backward 5 +125
9f72261d tests: gated_act_mxfp8 7 +934

Changes

f03bad29..9f72261d: 144 files, +110,370 / -0.

Group Files Lines Commit
Vendored package cake/_vendor/cake_gated_act_mxfp8_backend/ 131 +109,167 e6129b5d
cake/_vendor/VENDORED.json (ops.gated_act_mxfp8) 1 +144 e6129b5d
cake/gated_act_mxfp8.py (+29), cake/_impl/gated_act_mxfp8/__init__.py (+16), cake/_impl/gated_act_mxfp8/ops.py (+77) 3 +122 904c3bcc
_runtime/registry.py (OPS), README.md (Ops and Prebuild rows) 2 +3 904c3bcc
tests/cake/gated_act_mxfp8/ 7 +934 9f72261d

This layer adds no pyproject.toml extra and changes no tooling or CI files. Its only layer-0 edits are the "gated_act_mxfp8", entry in OPS and the two README rows.

Package (e6129b5d)

Package Upstream path Variant Files Tree Libraries sm_100a / sm_103a / sm_107a min_cuda
cake_gated_act_mxfp8_backend flashinfer/cake_gated_act_mxfp8_backend det (the only variant) 131 ffec0b8e 24 / 28 / 23 12.8 / 12.9 / 13.4
  • Vendored with this command:
    python tools/vendor_cake.py fi --checkout ../flashinfer \
        --commit c5db8d3ccd3fa3842812280f7f208718dc0b8610 \
        --op gated_act_mxfp8 \
        --package flashinfer/cake_gated_act_mxfp8_backend
    
  • Cake revision: ce6037e6fd6cd1be2b6e842ee079a616e4e821ff.
  • There are no allow entries: the package reads no environment variables, and flashinfer is imported only in the pinned cake_build.py loader.
  • The tree is identical at FlashInfer 7a13d7ba9b75c9bb9c02452df36e3f34417b5d57 and unchanged on FlashInfer main through 79cf05572cab. The last upstream change was perf(cake_gated_act_mxfp8): band-path program for the SM107 forward both entry below 2^20 flashinfer-ai/flashinfer#8023, which added the sm_107a band-path program ("Outputs are byte-identical to the previous version").
  • nvcc needed per architecture (the runtime minimum equals the manifest min_cuda): sm_100a 12.8, sm_103a 12.9, sm_107a 13.4. With CUDA 13.0, prebuild on SM107a raises RuntimeError before compiling anything.

Op (904c3bcc)

  • cake/gated_act_mxfp8.py is torch-free. OP declares one variant (det), the package and the two exports; there are no flags and no validate.
  • _impl/gated_act_mxfp8 installs the loader and holds libraries(arch), prebuild and a release that frees nothing.
  • Inputs: gated_input must be BF16 [M, 2K], contiguous and 32-byte aligned. M and K must be positive multiples of 128, and M * 2K <= 2^31.
  • Errors:
    • ValueError / TypeError for inputs the kernels do not take: a CPU tensor, the wrong dtype, a non-contiguous tensor, a bad shape (including M = 0), wrong 32-byte alignment, or neither rowwise nor colwise.
    • On a GPU that is not SM100a / SM103a / SM107a, the entry points raise the package's RuntimeError, and cake.prebuild("gated_act_mxfp8") raises NotImplementedError (the runtime's rule).
  • A call on a CUDA device that is not the current one is served.

Variant and routing

  • Every mode runs det: unconfigured, configured and deterministic mode. configure accepts det and rejects fast with ValueError. There is no no-op fast variant.
  • A call runs one program of its route. There are 6 routes: forward or backward × rowwise, colwise or both. The program is chosen by M * K alone. Every program of a route writes the same bytes, so the outputs are a function of the inputs and repeat bit for bit.
  • The host reads no tensor and never synchronizes the device. The kernels run on the current torch stream and write fresh output tensors, so calls can be captured in a CUDA graph.

Prebuild

  • cake.prebuild("gated_act_mxfp8", *, device=None, max_workers=None) takes no model parameters, because M and K are runtime arguments.
  • It builds every library of the device's architecture: for each of the six routes, every program the M * K rule can pick. That is 24 / 28 / 23 libraries (sm_100a / sm_103a / sm_107a). A new shape never builds a library. On GPU, the B200 cold run built 24 libraries into the op's private JIT cache, and so did the prebuild-first run.
  • Build time: on a CPU build node with nvcc 13.0 and 12 parallel compiles, all sm_100a libraries (24) built in 11.2 s and all sm_103a libraries (28) in 16.5 s. nvcc 13.0 refuses sm_107a, as designed.

Memory

  • The op uses no workspace (from the pool or owned by the package), no plan and no graph. Outputs are allocated per call, so the runtime pool and its bound (--cake-workspace-pool-bytes, default 0) do not apply.
  • release() frees nothing by design. The loaded libraries stay loaded because a captured CUDA graph may still launch them.

Tests

All tests are in tests/cake/gated_act_mxfp8/. _reference.py is a plain-torch reference ported from FlashInfer's, and _shapes.py holds the shapes.

  • CPU:
    • test_arguments.py: the declaration, the variant rules and the absence of registered arguments (table below), plus test_every_entry_point_is_defined_by_the_implementation and test_an_unknown_attribute_raises_attribute_error.
    • test_registry.py: library and route selection per architecture and the toolkit and device errors (table below), plus test_the_architectures_are_the_runtimes.
  • GPU, test_gated_act_mxfp8_gpu.py:
    • byte-exact comparisons with the reference, the MXFP8 bound against FP64, the host-sync and CUDA-graph checks, and the error checks (all in the table below);
    • test_det_repeats_bit_for_bit_in_process_and_in_a_fresh_process: 3 calls in one process plus 1 in a fresh process, compared by SHA-256, over 6 routes × 3 shapes;
    • test_calls_on_a_side_stream_match_the_default_stream: alternates 4 times;
    • test_output_layouts_and_disabled_outputs, test_the_libraries_build_through_the_runtime_loader_without_flashinfer;
    • test_a_call_on_a_device_that_is_not_current_matches_the_reference (multi_gpu, 2 GPUs).
  • GPU, test_prebuild_gpu.py: ROUTES is rowwise, colwise and both. Each route runs forward and backward at all 9 entry shapes, M × K = 2^14 … 2^28: (128,128), (256,256), (2048,1024), (2048,2048), (4096,2048), (4096,4096), (8192,4096), (8192,8192), (32768,8192). At these shapes the selection rule picks every program of each route on each architecture.
    • The test uses a private TVM_FFI_CACHE_DIR and a fresh cake_jit.load cache, so a library built or loaded by an earlier test cannot hide one that prebuild misses.
    • It also asserts that the private cache holds exactly len(libraries(arch)) .so files and that the load cache holds every program.

There is no earlier PR for this op. The cross-stack items from the review of the earlier stack apply as follows. All of these tests passed on B200 and GB300.

Item Here Tests
Inconsistent unconfigured default variant One variant, det; every mode runs det; configure rejects fast CPU test_arguments.py::test_every_mode_runs_det (2), ::test_configure_takes_det_and_rejects_fast, ::test_the_op_is_declared_with_one_det_variant_and_no_miles_selector
Mid-call nvcc compiles prebuild builds every library of the architecture; no model parameter CPU test_registry.py::test_prebuild_builds_exactly_the_architecture_libraries[sm_100a/sm_103a/sm_107a], ::test_the_routes_load_exactly_the_build_manifest_libraries[...], ::test_every_route_has_one_default_program_and_entry_shapes_select_every_program[...]; GPU test_prebuild_gpu.py::test_every_route_runs_without_a_build_after_one_prebuild, ::test_prebuild_returns_every_library_of_the_architecture_in_the_cache_and_is_idempotent, test_gated_act_mxfp8_gpu.py::test_a_new_shape_loads_no_library
Argument owner Registers no argument CPU test_arguments.py::test_no_argument_is_registered
Host synchronization (as on #3 and #11) None in any call GPU test_calls_never_synchronize_the_host (CUDA sync debug mode "error"), test_cuda_graph_replays_match_eager
T = 0 (as on #1 and #6) M = 0 raises ValueError (FlashInfer's contract; documented) GPU test_invalid_arguments_raise
Loose accuracy gates (#2, #10) Every program and entry point is compared byte-exact with the plain-torch reference. This is stricter than a relative gate that detects a 2 % error. Dequantized outputs stay within the MXFP8 rounding bound of FP64. GPU test_every_program_matches_the_reference (24 = 6 routes × 4 value sets), test_entry_points_match_the_reference (54 = 6 × 9 entry shapes), test_dequantized_outputs_are_within_the_mxfp8_bound_of_fp64 (12)
sm_107a needs nvcc ≥ 13.4 With an older toolkit, prebuild raises RuntimeError before any compile CPU test_registry.py::test_prebuild_refuses_sm107a_with_a_toolkit_older_than_cuda_13_4
Unsupported device prebuild raises NotImplementedError CPU test_registry.py::test_prebuild_on_an_unsupported_device_raises_not_implemented
Workspace streams No pooled buffer; side-stream calls match the default stream GPU test_calls_on_a_side_stream_match_the_default_stream

Validation

CPU, at this layer head and at each of its commits. Container radixark/miles:dev-202611032236 (Python 3.12.16, torch 2.14.1+cu130, tvm-ffi 0.1.11), with CUDA hidden.

Check Command e6129b5d 904c3bcc 9f72261d (head)
CI-style, torch-free venv pip install --no-deps -e . && pip install pytest; python -m pytest -rfEs -p no:cacheprovider tests 580 passed, 133 skipped, 1 failed (allowed) 586 passed, 135 skipped, 1 failed (allowed) 601 passed, 142 skipped, 0 failed
Container Python with torch PYTHONPATH=. python -m pytest -rfEs -p no:cacheprovider tests 1203 passed, 267 skipped, 1 failed (allowed) 1211 passed, 267 skipped, 1 failed (allowed) 1231 passed, 269 skipped, 0 failed
Vendored tree python tools/vendor_cake.py --check and python -m tools.vendor_cake --check rc 0 rc 0 rc 0
Style pre-commit run --all-files rc 0, no diff rc 0, no diff rc 0, no diff
Upstream python tools/vendor_cake.py --check gated_act_mxfp8 --upstream — — rc 0
Op tests only (container) python -m pytest -rs -m 'not multi_gpu' tests/cake/gated_act_mxfp8 — — 19 passed, 2 skipped
  • The allowed failures are exactly the layer-completeness tests that the per-commit rule names: tests/cake/_runtime/test_vendored.py::test_every_vendored_op_is_registered after the vendor commit, and tests/cake/_runtime/test_op_contract.py::test_the_op_has_cpu_tests_and_a_prebuild_test[gated_act_mxfp8] after the op commit. Any other failure fails the gate.
  • Against layer 11 (f03bad29): +20 passed / +9 skipped in the venv, +27 / +2 in the container.
  • At the stack top baba2c53, which has the same package tree and manifest entry, python tools/vendor_cake.py --check --upstream returns rc 0.
  • The skips are GPU tests on a node without CUDA, plus tests that need torch in the torch-free venv.

GPU, run on the stack top baba2c53, whose gated_act_mxfp8 files equal this layer head's. Both sites used the radixark/miles:dev-202611032236 container (amd64 on B200, arm64 on GB300) with Python 3.12.16, torch 2.14.1+cu130, nvcc 13.0.88, apache-tvm-ffi 0.1.11, triton 3.8.0 and pytest 9.2.0.

B200: 8× B200, sm_100a, 148 SMs, driver 580.82.07.

Run Result Wall
cold (-m 'not multi_gpu', empty cache) 119 passed, 0 skipped, 0 failed; 24 libraries built 40.1 s
warm 119 passed, 0 skipped 30.1 s
prebuild first (test_prebuild_gpu.py alone, new empty cache) 2 passed 35.1 s
multi_gpu (2 GPUs) 1 passed 10.0 s

GB300: 4× GB300, sm_103a, 152 SMs, driver 580.159.03.

Run Result Wall
cold (empty cache, test_prebuild_gpu.py first) 119 passed, 0 skipped, 0 failed 65.7 s
warm 119 passed, 0 skipped 26.3 s
multi_gpu (2 GPUs) 1 passed 4.3 s

Gates inside those runs, on both sites, cold and warm:

  • det bit for bit (test_det_repeats_bit_for_bit_in_process_and_in_a_fresh_process) passed.
  • Side stream (test_calls_on_a_side_stream_match_the_default_stream) passed.
  • No host synchronization and CUDA graph replay passed.
  • The byte-exact program and entry-point tests (24 + 54) and the MXFP8 bound (12) passed.
  • Multi-GPU, on a device that is not current, passed.
  • Prebuild (test_prebuild_gpu.py, 2 tests) passed. On B200 it ran as a separate first run on an empty cache; on GB300 it ran first in the cold run.

sm_107a: validation of this tree pending.

Performance

In the copied FlashInfer text, "this PR", "previous Cake" and "the package on FlashInfer main from #N" refer to that FlashInfer pull request and its predecessor package, not to this PR.

Package and variants

This layer vendors one package, cake_gated_act_mxfp8_backend (131 files), from flashinfer-ai/flashinfer c5db8d3ccd3fa3842812280f7f208718dc0b8610. Its package tree is ffec0b8e1990dd031802e63e7ad8d873c7e7af43, and the tree is the same at 7a13d7ba9b75c9bb9c02452df36e3f34417b5d57.

Role Package Status and source
deterministic cake_gated_act_mxfp8_backend Shipped. It is the only package and the op's only variant. A call runs one program chosen by M * K. The FlashInfer bodies state that the outputs are byte-identical across revisions (flashinfer-ai/flashinfer#8023: "Outputs are byte-identical to the previous version").
non-deterministic (fast) none FlashInfer ships no separate package for this kernel. flashinfer-ai/flashinfer#8023: "Cake has no other variants of this kernel beyond these six BF16-to-MXFP8 forms with the swizzled scale layout".
KCP none FlashInfer has no KCP package for this kernel at c5db8d3.

FlashInfer pull request of the vendored revision

FlashInfer PR Merge commit Merged (UTC) Package tree after the merge Relation to c5db8d3
flashinfer-ai/flashinfer#8023 78515ec558b9f8d3eaefc1f5e3b2c6e220aa01e3 2026-10-30T18:04:45Z ffec0b8e1990dd031802e63e7ad8d873c7e7af43 (= vendored) ancestor (compare: ahead 438, behind 0); last commit that changes the package before c5db8d3
flashinfer-ai/flashinfer#7880 55a28232ee1431da083b07cec9d17c8b3e7ca5fb 2026-10-29T19:44:29Z 56773e002bbcd88172ecf5c3847ac8142b4df8f9 (previous revision) ancestor

flashinfer-ai/flashinfer#8023 delivered exactly the vendored revision. It changes only sm_107a. Its merge commit adds one sm_107a library (cake_gated_act_mxfp8_e62d5f6dda3083f31950_sm_107a) to build.json and changes only sm_107a program selection in cake_jit.py. The body states: "The sm_100a and sm_103a programs are unchanged." and "The other libraries keep the same machine code."

Baselines

Baseline Status
Previous Cake version Reported. flashinfer-ai/flashinfer#8023 (R200) measures against the package from flashinfer-ai/flashinfer#7880. flashinfer-ai/flashinfer#7880 (B200, GB300) measures against the package from flashinfer-ai/flashinfer#7783.
Strongest non-Cake SOTA Missing. No FlashInfer body for this package reports a non-Cake baseline. flashinfer-ai/flashinfer#7305 measured against "the previous Cake version, the gated_act_mxfp8 kernels FlashInfer ships on main (added in flashinfer-ai/flashinfer#4638, SM107 enabled in flashinfer-ai/flashinfer#5392)". flashinfer-ai/flashinfer#4638 reports a "Baseline B" on B200 and GB300 for that first version, but it does not name the implementation. No body measures the vendored revision against a non-Cake kernel. No such ratio is computed here by chaining.
Vendored revision against the first version (flashinfer-ai/flashinfer#4638 kernels) Missing. No body measures it. Chained ratios are not reported and are not computed here.

Per-architecture coverage

Target GPU in the FlashInfer tables Libraries in build.json min_cuda in build.json Status Source
sm_100a B200, 148 SMs 24 12.8 measured; same programs as the vendored revision flashinfer-ai/flashinfer#7880 B200 table, CUDA 13.4
sm_103a GB300, 152 SMs 28 12.9 measured; same programs as the vendored revision (flashinfer-ai/flashinfer#7880: "The GB300 run used a package with the same machine code as this PR.") flashinfer-ai/flashinfer#7880 GB300 table, CUDA 13.4
sm_107a R200, 216 SMs 23 13.4 measured on the vendored revision flashinfer-ai/flashinfer#8023 R200 table, CUDA 13.4

The package has no other targets. flashinfer-ai/flashinfer#7305: "it ships on sm_100a (B200), sm_103a (GB300) and sm_107a (R200) only, with no family fallback to other architectures."

sm_107a (R200): the vendored revision against the previous Cake version

From flashinfer-ai/flashinfer#8023:

The baseline is the previous Cake version of this kernel, which is the package on FlashInfer main from flashinfer-ai/flashinfer#7880. With block-scaled inputs the changed owner row is faster: 128 x 128 forward both is 1.076x (2.36 us -> 2.20 us). The same entry also covers 256 x 256 and 512 x 512 forward both. Measured outside the owner list, these are 1.085x and 1.081x with block-scaled inputs. With unit-normal inputs the changed rows tie within the run-to-run spread (128 x 128 forward both is 1.004x). Every other row runs the same machine code as before and stays within run-to-run noise. Over all 30 rows, unit normals range from 0.998x to 1.004x and block-scaled inputs from 0.999x to 1.076x.

The table covers the owner rows on NVIDIA R200 (sm_107a, 216 SMs) with CUDA 13.4: 5 shapes x 6 passes, 30 rows. Both versions run in the same process on the same GPU, in 8 alternating paired rounds, with a cold L2 before every launch. Latency is the mean over pairs of each sample's median CUPTI kernel time, in microseconds. Speedup is the previous latency divided by this PR's latency, so a value above 1 means this PR is faster. Block-scaled inputs are normals with per-32-block scales spread over a wide exponent range. Outputs are byte-identical to the previous version, and the public API is unchanged.

Missing: the 256 x 256 and 512 x 512 forward both ratios (1.085x, 1.081x) have no absolute latencies in the body. They are outside the table and are not used here.

NVIDIA R200 (sm_107a, 216 SMs), CUDA 13.4

M x K Pass Unit normals: previous Cake (µs) This PR (µs) Speedup Block-scaled: previous Cake (µs) This PR (µs) Speedup
128 x 128 fwd rowwise 1.87 1.87 0.998x 2.01 2.01 0.999x
128 x 128 fwd colwise 2.03 2.03 0.998x 2.17 2.17 1.001x
128 x 128 fwd both 2.10 2.09 1.004x 2.36 2.20 1.076x
128 x 128 bwd rowwise 2.13 2.12 1.000x 2.31 2.31 1.000x
128 x 128 bwd colwise 2.23 2.23 1.001x 2.28 2.28 1.000x
128 x 128 bwd both 2.35 2.35 1.000x 2.45 2.45 0.999x
4096 x 2048 fwd rowwise 7.12 7.13 1.000x 8.12 8.11 1.001x
4096 x 2048 fwd colwise 7.18 7.18 1.000x 8.69 8.70 1.000x
4096 x 2048 fwd both 8.51 8.51 1.000x 10.07 10.07 1.000x
4096 x 2048 bwd rowwise 10.10 10.10 1.000x 10.35 10.35 1.000x
4096 x 2048 bwd colwise 10.63 10.63 1.000x 11.24 11.25 1.000x
4096 x 2048 bwd both 12.67 12.66 1.000x 13.31 13.29 1.002x
4096 x 7168 fwd rowwise 17.75 17.75 1.000x 19.17 19.17 1.000x
4096 x 7168 fwd colwise 18.04 18.04 1.000x 20.95 20.94 1.000x
4096 x 7168 fwd both 21.02 21.02 1.000x 25.41 25.40 1.000x
4096 x 7168 bwd rowwise 27.05 27.05 1.000x 27.71 27.72 1.000x
4096 x 7168 bwd colwise 27.51 27.51 1.000x 29.19 29.20 1.000x
4096 x 7168 bwd both 33.91 33.90 1.000x 34.94 34.86 1.002x
16384 x 7168 fwd rowwise 56.92 56.93 1.000x 64.34 64.30 1.001x
16384 x 7168 fwd colwise 57.79 57.78 1.000x 67.11 67.11 1.000x
16384 x 7168 fwd both 70.39 70.39 1.000x 75.77 75.74 1.000x
16384 x 7168 bwd rowwise 92.87 92.86 1.000x 96.73 96.77 1.000x
16384 x 7168 bwd colwise 93.98 93.98 1.000x 103.08 103.05 1.000x
16384 x 7168 bwd both 123.56 123.55 1.000x 125.56 125.50 1.000x
131072 x 8192 fwd rowwise 462.66 462.66 1.000x 515.03 514.74 1.001x
131072 x 8192 fwd colwise 483.20 483.20 1.000x 539.91 539.75 1.000x
131072 x 8192 fwd both 610.50 610.56 1.000x 621.14 621.18 1.000x
131072 x 8192 bwd rowwise 796.30 796.29 1.000x 829.40 829.42 1.000x
131072 x 8192 bwd colwise 810.45 810.46 1.000x 869.06 869.34 1.000x
131072 x 8192 bwd both 1095.04 1095.07 1.000x 1109.63 1109.66 1.000x

sm_100a (B200) and sm_103a (GB300): the vendored programs against the previous Cake version

From flashinfer-ai/flashinfer#7880. The "This PR" columns run the same sm_100a and sm_103a programs as the vendored revision.

The baseline is the previous Cake version of this kernel, which is the package on FlashInfer main from flashinfer-ai/flashinfer#7783. With block-scaled inputs the changed rows are faster: B200 4096 x 2048 backward colwise is 1.112x, and GB300 4096 x 7168 backward both is 1.030x. With unit-normal inputs, both changed rows are ties within the run-to-run spread. Every other row runs the same machine code as before and stays within run-to-run noise. On B200, the 131072 x 8192 block-scaled rows read 0.996x to 1.014x under the GPU power cap, with the same program on both sides. That is noise, not a gain or a loss. With unit-normal inputs, all rows are ties within the spread: 0.992x to 1.010x on B200 and 0.998x to 1.001x on GB300.

Both versions run in the same process on the same GPU, in 8 alternating paired rounds, with a cold L2 before every launch. Latency is the mean over pairs of each sample's median CUPTI kernel time, in microseconds. Speedup is the previous latency divided by this PR's latency, so a value above 1 means this PR is faster. Block-scaled inputs are normals with per-32-block scales spread over a wide exponent range. The GB300 run used a package with the same machine code as this PR. The package goes from 72 to 74 per-arch programs across 61 records. One record is new, and one existing sm_100a record gains sm_103a. The exporter regenerates the other kernel.cu files, and their machine code is unchanged. Outputs are byte-identical to the previous version, and the public API is unchanged.

NVIDIA B200 (sm_100a, 148 SMs), CUDA 13.4

M x K Pass Unit normals: previous Cake (µs) This PR (µs) Speedup Block-scaled: previous Cake (µs) This PR (µs) Speedup
128 x 128 fwd rowwise 2.27 2.27 1.001x 2.62 2.62 1.000x
128 x 128 fwd colwise 2.33 2.33 0.999x 2.65 2.65 1.000x
128 x 128 fwd both 2.46 2.46 1.000x 2.80 2.79 1.001x
128 x 128 bwd rowwise 2.43 2.42 1.000x 2.64 2.64 1.000x
128 x 128 bwd colwise 2.54 2.54 1.000x 2.68 2.68 1.000x
128 x 128 bwd both 2.72 2.72 1.000x 2.88 2.88 0.999x
4096 x 2048 fwd rowwise 9.76 9.76 1.000x 10.00 10.00 1.000x
4096 x 2048 fwd colwise 10.46 10.46 1.000x 12.85 12.85 1.000x
4096 x 2048 fwd both 11.66 11.66 1.000x 13.68 13.67 1.000x
4096 x 2048 bwd rowwise 14.08 14.08 1.000x 14.56 14.56 1.000x
4096 x 2048 bwd colwise 15.02 15.02 1.000x 18.54 16.67 1.112x
4096 x 2048 bwd both 18.00 18.00 1.000x 19.64 19.64 1.000x
4096 x 7168 fwd rowwise 25.39 25.38 1.000x 25.08 25.08 1.000x
4096 x 7168 fwd colwise 26.51 26.50 1.000x 31.97 31.97 1.000x
4096 x 7168 fwd both 31.28 31.29 1.000x 35.69 35.69 1.000x
4096 x 7168 bwd rowwise 39.41 39.41 1.000x 40.55 40.56 1.000x
4096 x 7168 bwd colwise 41.45 41.46 1.000x 46.07 46.08 1.000x
4096 x 7168 bwd both 50.57 50.57 1.000x 51.91 51.91 1.000x
16384 x 7168 fwd rowwise 90.18 90.19 1.000x 89.27 89.26 1.000x
16384 x 7168 fwd colwise 92.21 92.22 1.000x 101.59 101.54 1.001x
16384 x 7168 fwd both 108.83 108.82 1.000x 122.87 122.87 1.000x
16384 x 7168 bwd rowwise 140.48 140.47 1.000x 144.19 144.20 1.000x
16384 x 7168 bwd colwise 144.72 144.73 1.000x 159.71 159.70 1.000x
16384 x 7168 bwd both 186.45 186.56 0.999x 185.77 185.77 1.000x
131072 x 8192 fwd rowwise 761.64 761.01 1.001x 822.26 811.22 1.014x
131072 x 8192 fwd colwise 794.20 791.10 1.004x 921.09 918.40 1.003x
131072 x 8192 fwd both 987.24 983.91 1.003x 1143.59 1148.47 0.996x
131072 x 8192 bwd rowwise 1219.66 1229.47 0.992x 1403.14 1393.85 1.007x
131072 x 8192 bwd colwise 1380.15 1370.43 1.007x 1540.72 1531.41 1.006x
131072 x 8192 bwd both 1743.41 1725.95 1.010x 1991.55 1968.00 1.012x

NVIDIA GB300 (sm_103a, 152 SMs), CUDA 13.4

M x K Pass Unit normals: previous Cake (µs) This PR (µs) Speedup Block-scaled: previous Cake (µs) This PR (µs) Speedup
128 x 128 fwd rowwise 1.99 2.00 0.998x 2.29 2.29 1.001x
128 x 128 fwd colwise 2.06 2.06 1.000x 2.36 2.36 1.000x
128 x 128 fwd both 2.16 2.16 1.000x 2.46 2.46 1.000x
128 x 128 bwd rowwise 2.13 2.13 1.001x 2.23 2.23 0.999x
128 x 128 bwd colwise 2.27 2.27 0.998x 2.42 2.42 0.999x
128 x 128 bwd both 2.40 2.40 0.998x 2.56 2.56 1.000x
4096 x 2048 fwd rowwise 9.04 9.04 1.000x 9.51 9.51 1.000x
4096 x 2048 fwd colwise 9.71 9.71 1.000x 12.36 12.35 1.000x
4096 x 2048 fwd both 10.99 10.98 1.000x 13.31 13.30 1.001x
4096 x 2048 bwd rowwise 13.49 13.48 1.001x 13.80 13.80 1.000x
4096 x 2048 bwd colwise 14.19 14.19 1.000x 17.15 17.16 1.000x
4096 x 2048 bwd both 16.97 16.97 1.000x 19.25 19.25 1.000x
4096 x 7168 fwd rowwise 24.04 24.03 1.000x 25.06 25.07 1.000x
4096 x 7168 fwd colwise 25.37 25.37 1.000x 29.32 29.33 1.000x
4096 x 7168 fwd both 29.97 29.97 1.000x 34.68 34.68 1.000x
4096 x 7168 bwd rowwise 37.91 37.91 1.000x 39.65 39.65 1.000x
4096 x 7168 bwd colwise 39.67 39.68 1.000x 43.26 43.26 1.000x
4096 x 7168 bwd both 47.84 47.80 1.001x 50.10 48.65 1.030x
16384 x 7168 fwd rowwise 86.77 86.80 1.000x 90.66 90.66 1.000x
16384 x 7168 fwd colwise 90.03 90.00 1.000x 96.05 96.04 1.000x
16384 x 7168 fwd both 104.99 104.94 1.000x 113.59 113.59 1.000x
16384 x 7168 bwd rowwise 135.37 135.37 1.000x 139.17 139.16 1.000x
16384 x 7168 bwd colwise 139.23 139.23 1.000x 148.32 148.31 1.000x
16384 x 7168 bwd both 175.40 175.43 1.000x 176.95 176.97 1.000x
131072 x 8192 fwd rowwise 738.02 738.06 1.000x 755.48 755.40 1.000x
131072 x 8192 fwd colwise 756.59 756.59 1.000x 816.21 816.21 1.000x
131072 x 8192 fwd both 906.47 906.42 1.000x 954.61 954.76 1.000x
131072 x 8192 bwd rowwise 1170.76 1170.73 1.000x 1203.50 1203.53 1.000x
131072 x 8192 bwd colwise 1220.39 1220.37 1.000x 1293.39 1293.41 1.000x
131072 x 8192 bwd both 1563.22 1563.39 1.000x 1559.46 1559.47 1.000x

Earlier revisions (context only, not the vendored revision)

Each of these FlashInfer PRs is an ancestor of c5db8d3 and measured its own head against the package before it. None of them measured the vendored revision.

FlashInfer PR Merge commit Title
flashinfer-ai/flashinfer#7783 0a99c904c4b3852154e41280bf5ca86de983f7d7 perf(cake_gated_act_mxfp8): band-path programs for three SM107 entries
flashinfer-ai/flashinfer#7770 d95ab212283e1c9fb87576768fd708acd13dd714 perf(cake_gated_act_mxfp8): packed-sigmoid band-path programs for five SM103 entries
flashinfer-ai/flashinfer#7702 ea2d6e96717f9eb9870c221772acb27551eafe49 perf(cake_gated_act_mxfp8): packed-sigmoid band-path programs for mid-size SM100 problems and a band-path backward colwise fallback (SM100 / SM103)
flashinfer-ai/flashinfer#7660 565387490276d0ecc9939dea677bf127864d1857 perf(cake_gated_act_mxfp8): packed-sigmoid band-path programs for large SM100 problems and band-path fallbacks (SM100 / SM107)
flashinfer-ai/flashinfer#7568 1d74db1e9dfbc6e0d221d2ed3a34015bdcc0007c perf(cake_gated_act_mxfp8): register-fitted band-path and packed-sigmoid programs in eight M * K ranges (SM100 / SM103)
flashinfer-ai/flashinfer#7492 ecc3b2f8e378fb6ce0cabc57040c88357e38bce5 perf(cake_gated_act_mxfp8): band-path forms of the guarded sigmoid where they pay (SM100 / SM103 / SM107)
flashinfer-ai/flashinfer#7396 93e5508a1504850c7e641d82c852ec4b665770e1 perf(cake_gated_act_mxfp8): packed f32x2 sigmoid where it pays (SM100 / SM103 / SM107)
flashinfer-ai/flashinfer#7346 0b352a6333c706d9910c622de978e934f8be7c33 perf(cake_gated_act_mxfp8): narrow tiles for small problems and four rows per thread for large ones
flashinfer-ai/flashinfer#7305 bfe3de571c093e4f516a813c9bead22c2486d898 perf(cake_gated_act_mxfp8): faster fused SwiGLU + MXFP8 quantization forward and backward (SM100 / SM103 / SM107)

flashinfer-ai/flashinfer#7305 introduced the package. Its head is not the vendored revision. Its summary against the flashinfer-ai/flashinfer#4638 kernels:

The baseline is the previous Cake version, the gated_act_mxfp8 kernels FlashInfer ships on main (added in flashinfer-ai/flashinfer#4638, SM107 enabled in flashinfer-ai/flashinfer#5392). Against that baseline, the PR is 1.075-1.501x faster on B200 (forward 1.196-1.501x, backward 1.075-1.474x), 1.044-1.411x faster on GB300 (forward 1.169-1.411x, backward 1.044-1.383x) and 1.094-1.697x faster on R200 (forward 1.331-1.697x, backward 1.094-1.427x). Every row of every table is faster.

Notes

  • sm_107a:
  • Limits:
    • gated_input must be contiguous, 32-byte aligned BF16; M and K must be positive multiples of 128, with M * 2K <= 2^31.
    • M = 0 is rejected.
    • det is the only variant.
  • The op is not wired into miles: there is no flag and no call site.

🤖 Generated with Claude Code

averyhNV and others added 30 commits November 4, 2026 23:14
Vendor FlashInfer's two Cake short-conv packages at FlashInfer
c5db8d3ccd3fa3842812280f7f208718dc0b8610 into miles_ext_kernels/cake/_vendor/, one level deep:

- cake_causal_conv1d_det_train_backend (flashinfer/cake_causal_conv1d_det_train_backend,
  87 files): det, fixed-order weight-gradient reduction, with its context-parallel route
- cake_causal_conv1d_train_backend (flashinfer/experimental/cake/cake_causal_conv1d_train_backend,
  75 files): fast, unordered weight-gradient reduction, with its context-parallel route

Both kcp.py modules read one environment variable, a private test hook of the package; the manifest
lists them in allow.env_read. Every file is byte for byte FlashInfer's.

Command (from the repository root, a FlashInfer clone next to it):

    python tools/vendor_cake.py fi --checkout ../flashinfer \
        --commit c5db8d3ccd3fa3842812280f7f208718dc0b8610 --op conv1d \
        --package flashinfer/cake_causal_conv1d_det_train_backend \
        --package flashinfer/experimental/cake/cake_causal_conv1d_train_backend \
        --allow-env-read cake_causal_conv1d_det_train_backend:kcp.py \
        --allow-env-read cake_causal_conv1d_train_backend:kcp.py

Co-Authored-By: Claude <noreply@anthropic.com>
The two Cake MoE grouped GEMM packages of FlashInfer
c5db8d3ccd3fa3842812280f7f208718dc0b8610, byte for byte, one level deep
under miles_ext_kernels/cake/_vendor/ and recorded under
ops.moe_grouped_gemm of _vendor/VENDORED.json:

    cake_moe_grouped_det_gemm  det   flashinfer/experimental/cake_moe_grouped_det_gemm
    cake_moe_grouped_gemm      fast  flashinfer/experimental/cake/cake_moe_grouped_gemm

Every blob and both Cake revisions are the ones the previous layout
vendored from the same commit. No vendored module reads the environment
or imports flashinfer, so neither package has an allow entry.

Vendored with:

    python tools/vendor_cake.py fi --checkout ../flashinfer \
        --commit c5db8d3ccd3fa3842812280f7f208718dc0b8610 --op moe_grouped_gemm \
        --package flashinfer/experimental/cake_moe_grouped_det_gemm \
        --package flashinfer/experimental/cake/cake_moe_grouped_gemm

Co-Authored-By: Claude <noreply@anthropic.com>
Vendor FlashInfer's chunked LM-head training packages at c5db8d3, one per
variant, into miles_ext_kernels/cake/_vendor/, with the lm_head block of
VENDORED.json:

- cake_lm_head_det_loss (flashinfer/experimental/cake_lm_head_det_loss,
  102 files): det
- cake_lm_head_loss (flashinfer/experimental/cake/cake_lm_head_loss,
  126 files): fast

Both package trees equal the upstream directories and the trees this op
shipped before (the same FlashInfer commit), blob for blob. Command, run
from the repository root with a FlashInfer checkout at ../flashinfer:

    python tools/vendor_cake.py fi --checkout ../flashinfer \
        --commit c5db8d3ccd3fa3842812280f7f208718dc0b8610 --op lm_head \
        --package flashinfer/experimental/cake_lm_head_det_loss \
        --package flashinfer/experimental/cake/cake_lm_head_loss

Co-Authored-By: Claude <noreply@anthropic.com>
Vendor FlashInfer's Cake packed causal GQA training attention package
(head dim 256, SM100a / SM103a / SM107a) from flashinfer-ai/flashinfer
c5db8d3ccd3fa3842812280f7f208718dc0b8610

    flashinfer/experimental/cake_gqa_train

byte for byte into miles_ext_kernels/cake/_vendor/cake_gqa_train/, and
record it as the gqa op's block of _vendor/VENDORED.json, with

    python tools/vendor_cake.py fi --checkout ../flashinfer \
        --commit c5db8d3ccd3fa3842812280f7f208718dc0b8610 --op gqa \
        --package flashinfer/experimental/cake_gqa_train

run in this repository with a FlashInfer checkout of that commit next to
it. The package has 21 files; their git blobs and its Cake revision
(7ab5de0c9d4e42a367ba3e9e218d98208f9bd8fa) are the ones it had before
this layout, from the same FlashInfer commit. Nothing outside _vendor/
changes; the op that loads the package comes in the next commit.

Co-Authored-By: Claude <noreply@anthropic.com>
miles_ext_kernels.cake.moe_grouped_gemm declares the op and imports
nothing but the runtime's spec: det and fast, selected by
--moe-grouped-gemm-backend cake (which requires --train-backend
torchtitan), the two vendored packages, and the entry points
grouped_mm, check_grouped_dims and max_grouped_rows. The implementation
lives in cake/_impl/moe_grouped_gemm/ and builds its libraries through
the runtime's loader.

Changes from the previous layout:

- grouped_mm reads the configured variant at every forward, and its
  backward runs the package of its forward. The variant= argument, the
  module-level VARIANT and the op's own configure are gone: miles
  configures the process with miles_ext_kernels.configure(...). A
  process that never configured the op runs det; miles' command line
  still defaults to fast. A fast call under
  torch.use_deterministic_algorithms(True) runs det and logs it once,
  as before: the fast weight gradient raises torch's alert there.
- The 32-bit limits are checked before anything runs. The kernels
  address each output with 32-bit element offsets: sum_m * N,
  sum_m * K and E * N * K must stay at most 2**31 - 1. grouped_mm checks
  all three at the forward call, so a call whose input gradient would
  overflow fails there instead of in the backward after the forward ran
  (at N = 4096, K = 7168, 300,000 rows used to pass the forward).
  check_grouped_dims takes num_groups and max_rows, so the model build
  can refuse a configuration whose most rows per call exceed the bound,
  and max_grouped_rows(N, K) names the bound (299,593 rows at N = 4096,
  K = 7168).
- prebuild("moe_grouped_gemm") builds both variants' programs for the
  device (no variant argument, no model parameter) and, on sm_100a and
  sm_103a, the input gradient's probe of the memory domains, which the
  first probed call used to compile: after it no call compiles.
- release() drops the workspaces both packages keep per device and
  stream, with the queue splits probed on them.
- An unsupported device raises NotImplementedError (was RuntimeError).

README: the op's rows in the Ops and Prebuild tables.

Co-Authored-By: Claude <noreply@anthropic.com>
The three FlashInfer Cake mHC training packages at c5db8d3ccd3fa3842812280f7f208718dc0b8610,
byte for byte, each one level under miles_ext_kernels/cake/_vendor/ and in the mhc block of
VENDORED.json:

- cake_mhc_det_train_backend (flashinfer/cake_mhc_det_train_backend): det, the forward and a
  backward whose parameter gradients are fixed-order reductions; 221 files.
- cake_mhc_train_backend (flashinfer/experimental/cake/cake_mhc_train_backend): fast, copies of
  the det forward programs and a backward that adds the dfn partials with FP32 atomics; 181 files.
- cake_mhc_det_train_head_backend (flashinfer/cake_mhc_det_train_head_backend): the model head's
  stream mean and its gradient; 12 files.

The package trees are the ones this op shipped before the layout change (same git trees). No
vendored module reads the environment or imports flashinfer, so the entries list no allow hooks.

Vendored by running, in this repository with a FlashInfer checkout of that commit next to it:

    python tools/vendor_cake.py fi --checkout ../flashinfer \
        --commit c5db8d3ccd3fa3842812280f7f208718dc0b8610 --op mhc \
        --package flashinfer/cake_mhc_det_train_backend \
        --package flashinfer/cake_mhc_det_train_head_backend \
        --package flashinfer/experimental/cake/cake_mhc_train_backend

Co-Authored-By: Claude <noreply@anthropic.com>
miles_ext_kernels.cake.mhc declares the op (torch-free): variants det and fast, selected by
--mhc-backend cake, --cake-mhc-variant, the three vendored packages, and the entry points
mhc_prepare, mhc_finish, mhc_head, is_supported, supported_hidden_sizes, HIDDEN_SIZES and STREAMS,
loaded from miles_ext_kernels.cake._impl.mhc on first use. Its validate rejects a --hidden-size
the kernels do not run.

miles_ext_kernels.cake._impl.mhc.ops routes a connection to the det or the fast package by the
variant configured when mhc_prepare runs; the handle carries it to mhc_finish and the backward.

Changes in behaviour:

- A process that never configured mhc runs det, and configure(deterministic_mode=True) without a
  variant runs det (it ran fast). An explicit fast in deterministic mode is rejected.
- The variant comes from the configuration only: torch.use_deterministic_algorithms(True) no longer
  turns a fast call into det; the fast backward then raises torch's alert for an operation without
  a deterministic implementation. The per-call variant argument is gone.
- prebuild("mhc", *, device=None, max_workers=None, hidden, sinkhorn_iters) takes both model
  parameters, without defaults, and builds every library of both variants and the head at that
  shape. A Sinkhorn count other than 20 runs run-time-count programs of the coefficient and
  token-owner passes, which a prebuild at the default count left to compile inside the first call.
  The epsilons are launch arguments and select no library.
- BF16 fn, scale and base (FSDP2 with param_dtype=torch.bfloat16) are cast to FP32 at the call,
  which is exact; their gradients return in BF16. They raised ValueError.
- Workspaces come from the runtime's workspace pool: each launch of the forward, the statistics
  replay and the backward takes a buffer when it runs (roles det-fwd, det-bwd, fast-fwd, fast-bwd,
  sized for the call's power-of-two token-count bucket), so they stay under
  --cake-workspace-pool-bytes and miles_ext_kernels.release() frees them. The packages' own
  per-device default workspaces, which lived for the whole process, are no longer allocated.
- release() drops the launch plans and the loaded libraries; the next call reloads the libraries
  from the cache.

The packages resolve every launch workspace through one function, which the op replaces with the
pooled one. The kernels' work tickets, the first 256 bytes of every workspace layout, must be zero
before a launch, and the content of a workspace is undefined: every launch zeroes them first (256
bytes on the stream of the call), from the pool or not.

Co-Authored-By: Claude <noreply@anthropic.com>
miles_ext_kernels.cake.gqa declares the op (torch-free) and
miles_ext_kernels.cake._impl.gqa runs FlashInfer's cake_gqa_train
package through the runtime's loader: varlen_gqa_attention, packed
causal GQA attention over variable-length segments, BF16 q
[T_q, H_q, 256] and k / v [T_k, H_kv, 256] with an even number of query
heads per key/value head, forward and backward, on SM100a, SM103a and
SM107a. miles selects it with --full-attention-backend cake.

Variants. The op ships one variant, fast: the output, the LSE and dK /
dV repeat bit for bit run to run, dQ is summed with FP32 atomic adds.
There is no det variant, so the runtime rejects the op under
--deterministic-mode, and a process configured with
deterministic_mode=True raises at the call. A process that never
configured the op runs fast.

max_seqlen_k is now a required keyword-only host int and a documented
hard contract: at least every segment's key count, as FlashAttention's.
It sizes the backward grid, so a smaller value leaves key blocks out of
the backward and gives wrong dQ / dK / dV without an error, while the
output is unaffected. The kernels never read the boundaries on the
host; the new option --cake-gqa-check-seqlens (configure
cake_gqa_check_seqlens=True) checks at every call that cu_seqlens_q /
cu_seqlens_k start at 0, never decrease and end at the token counts,
and that no segment has more keys than max_seqlen_k, at the price of
one device synchronization per call. It is off by default.

The implicit None bound is gone: it sized the backward grid by T_k,
which is correct but launches CTA pairs that only retire, and made the
backward slower the more segments a call packs. A caller without the
longest segment can still pass T_k explicitly. The output, the LSE and
dK / dV do not depend on the bound given.

check_supported(num_heads_q, num_heads_kv, head_dim=256) and
is_supported(...) tell, without torch, whether the kernels run a head
layout. validate_args applies them to the heads one tensor-parallel
rank of miles holds (num_attention_heads / TP query heads over
max(num_query_groups, TP) / TP key/value heads, head dim from
--kv-channels or hidden_size / num_attention_heads), so a layout such as
24 query heads over 4 groups at TP 8 (3 query heads over 1 key/value
head per rank) is rejected at startup rather than at the first forward.

Memory. Each backward takes its scratch from the runtime's workspace
pool on the current stream, and each forward zeroes a fresh tile
counter; nothing goes through the package's per-stream default
workspaces, which grow to the largest call and live as long as the
process. release() therefore frees all of it, and calls on two streams
never share a buffer. prebuild("gqa") builds the four libraries of the
forward and the BF16 dK / dV backward for the device's architecture; it
takes no model parameter.

The op's names move from miles_ext_kernels.gqa:
SUPPORTED_CAPABILITIES, device_arch and prebuild are the runtime's
ARCH_BY_CAPABILITY, device_arch and prebuild("gqa"). The README lists
the op in its Ops and Prebuild tables.

Co-Authored-By: Claude <noreply@anthropic.com>
tests/cake/gqa/, CPU:

- test_arguments.py (torch-free): the op registers one variant and no
  variant flag; --cake-gqa-check-seqlens is off by default and needs
  --full-attention-backend cake; --deterministic-mode (also through the
  typed configuration) rejects the op; check_supported / is_supported
  on head layouts; validate_args on the heads of one tensor-parallel
  rank (24 query heads over 4 groups at TP 8 and 8 over 8 at TP 8
  rejected, 16 over 4 at TP 8 accepted, head dim from --kv-channels or
  hidden_size / num_attention_heads); configure and option.
- test_gqa_cpu_plan.py (torch-free): every stage the entry point
  launches has one library per architecture, which the oldest toolkit
  the runtime accepts for it (MIN_CUDA) builds; the grids cover every
  tile; the backward grid covers the key blocks of max_seqlen_k and no
  more; the scratch layout.
- test_gqa_host.py (torch on the CPU): deterministic mode raises at the
  call and configure() lets the next call through; max_seqlen_k is a
  required host int; check_seqlens accepts the contract and rejects a
  bound below the longest segment and malformed boundaries; the option
  runs the check before any launch; with the pool on, the backward
  scratch is one pooled buffer in the package's layout, and at the
  default bound, 0, each backward takes a buffer of its own that the
  pool does not keep; prebuild takes no model parameter,
  raises NotImplementedError on another GPU and RuntimeError when the
  toolkit is older than CUDA 13.4 on SM107a.

GPU (skip without SM100a / SM103a / SM107a):

- test_gqa_gpu.py: FlashInfer's suite layouts and miles' head pairs
  against an FP64 reference (assert_rel_rms) within FlashInfer's budget
  capped at twice the error measured on a B200, noted next to it:
  FlashInfer's budget for out, dQ, dK and dV (1.08 to 1.12 times the
  measured error), twice the measured error for the LSE, whose errors
  are at FP32 rounding;
  out, LSE, dK and dV bit for bit run to run in one process and against
  a new process, for any valid max_seqlen_k (exact, larger, T_k), on
  alternating default and side streams, and pooled against the pool
  turned off;
  dQ within its FP32 reordering; the check rejects max_seqlen_k 1024,
  1920 and 2047 on 2048-key segments and changes no bit when valid;
  document isolation; checkpointing; no device synchronization in
  steady state (and one with the check); with the pool on, the pooled
  scratch and release(); rejected operands; two GPUs give the same bits
  (multi_gpu).
- test_prebuild_gpu.py: ROUTES for every call-time path (exact and T_k
  max_seqlen_k, groups 2 / 4 / 6 / 8, keys fewer than queries, empty
  segments, long keys, one segment, no query, no key, a softmax scale,
  the check option, a side stream, the pool on) after one prebuild
  under no_build(); prebuild returns the four libraries; a fresh
  process loads only those and never imports flashinfer.

Co-Authored-By: Claude <noreply@anthropic.com>
CPU (no GPU): the arguments and the variant rules through the hooks miles calls, deterministic mode
without a variant included; the vendored packages against the routes, constants and hidden sizes
the op relies on; what prebuild builds per Sinkhorn count, checked against the programs the host
rules select at every token count; BF16 parameters reaching the kernels as FP32; the variant read
at each call; with the pool on, every launch workspace taken from the pool, its tickets zeroed at
every launch, and at the default bound, 0, none kept; what release drops.

GPU: det and fast against an FP64 reference at every hidden size the device serves, the fast
outputs against det's bits, another Sinkhorn count and other epsilons, BF16 parameters, det bit for
bit in a new process, on a side stream and on a second GPU (multi_gpu), with the pool on: the bits
under any pool bound, from a buffer a larger call sized and with interleaved connections, release;
the head, and test_prebuild_gpu.py: ROUTES over every call-time switch at the Sinkhorn count 7,
checked from an empty library cache.

Co-Authored-By: Claude <noreply@anthropic.com>
miles_ext_kernels/cake/conv1d.py declares the op: variants det and fast, selected by
--linear-attention-conv-backend cake, the option --cake-conv1d-silu-grad-preactivation, the two
vendored packages and the entry points causal_conv1d, causal_conv1d_kcp, causal_conv1d_kcp_multi and
check_supported. miles_ext_kernels/cake/_impl/conv1d/ implements them on the runtime's loader, config
and prebuild. The op is appended to OPS and to the README's Ops and Prebuild tables.

Changes in behavior:

- A process that never configured the op runs det; it ran fast. miles' command line still defaults
  to fast: validate_args resolves --cake-conv1d-variant to fast and writes it back for configure.
- Each call reads its variant and SiLU' policy when it runs; the backward runs those of its forward.
- The entry points take no variant argument: only configure selects the variant, so a process
  configured with deterministic_mode runs det on every call. A call could pass variant="fast" before.
- --cake-conv1d-silu-grad-preactivation defaults to bf16; it had no default. Without
  --linear-attention-conv-backend cake, an explicit bf16 is now accepted (it raised before); any
  other value still requires the cake backend.
- causal_conv1d without cu_seqlens builds the prefix sums of its one document on the device. It copied
  them from pageable host memory before, which synchronized the host on every call.
- check_supported(..., has_bias=True) raises: the entry points take no bias, so a biased layer
  would lose its bias.
- prebuild("conv1d", weight_dtype=...) builds every library of both variants, both SiLU' policies and
  the context-parallel route. It replaces prebuild(dtype) and prebuild_kcp().
- release() drops the packages' host plans.

Co-Authored-By: Claude <noreply@anthropic.com>
Move the conv1d tests to tests/cake/conv1d/ and run them through the runtime (configure, variant,
prebuild, release).

- CPU: the arguments and the configuration (det unconfigured, fast from miles' command line, an
  explicit default SiLU' policy without the cake backend), the variant and SiLU' policy read at each
  call, no entry point taking a variant (det on every call under deterministic_mode), the
  one-document cu_seqlens built on the device, the rejected bias, prebuild's arguments and its
  libraries against the packages' program selection on sm_100a, sm_103a and sm_107a, release, the
  vendored packages and their loading without flashinfer.
- GPU: the accuracy and determinism tests as before, each variant selected with configure, and new
  ones: det bitwise in a new process, cu_seqlens=None without a host synchronization or a wait for
  queued work, calls on a side stream interleaved with the default stream, an unconfigured process
  running det, and test_prebuild_gpu.py, whose ROUTES cover every call-time path and run under
  no_build() after one prebuild, for each taps dtype in a new process with an empty library cache
  of its own.
- The multi-GPU context-parallel test is marked multi_gpu.

Co-Authored-By: Claude <noreply@anthropic.com>
tests/cake/moe_grouped_gemm/:

- test_arguments.py (CPU, torch-free): the variant flag through miles'
  hooks, deterministic mode, the torchtitan requirement, configure, and
  an import without torch.
- test_limits.py (CPU, torch): the 32-bit bounds of check_grouped_dims
  and max_grouped_rows, and grouped_mm refusing at the forward, on meta
  tensors, a call whose input gradient or weight gradient would
  overflow.
- test_moe_grouped_gemm_gpu.py (GPU): every output and gradient against
  a per-group FP32 reference with relative-RMS budgets that fail at a
  2 % error (assert_close(atol=rtol=1e-2) passed y scaled by 1.05);
  det repeating bit for bit in one process and in a new one; fast
  giving det's forward and input-gradient bits and its unordered
  weight-gradient tail within the budget; the variant read at each
  call; det under torch's deterministic algorithms; calls alternating
  between the default and a side stream; release(); the 32-bit row
  bound at N = 4096, K = 7168 (300,000 rows refused at the forward;
  at 299,593 rows the last 2,048 rows of the output and of the input
  gradient and the whole weight gradient within the budget against
  closed-form FP64 references); empty input; padding rows past
  offs[-1]; refused calls; the toolkit check; a call for a device
  other than the current one (multi_gpu).
- test_prebuild_gpu.py (GPU): ROUTES (every layer shape, empty experts,
  no rows, uneven routing over a sweep of row counts, padding rows, the
  512-wide k tile, the stage twins at many rows per expert, a side
  stream) run with every variant after one prebuild, with no nvcc and
  no inline tvm-ffi build; a variant configured after a call runs
  without a build; every program the planners bind is prebuilt.

Co-Authored-By: Claude <noreply@anthropic.com>
The four Cake KDA training packages of FlashInfer 7a13d7ba9b75c9bb9c02452df36e3f34417b5d57, byte for byte, one
level deep under miles_ext_kernels/cake/_vendor/ and recorded under ops.kda of VENDORED.json:

- cake_kda_det_train (flashinfer/experimental/cake_kda_det_train): det, single rank.
- cake_kda_det_train_kcp (flashinfer/experimental/cake_kda_det_train_kcp): det, context parallel.
- cake_kda_train (flashinfer/experimental/cake/cake_kda_train): fast (deterministic=False), single rank.
- cake_kda_train_kcp (flashinfer/experimental/cake_kda_train_kcp): fast, context parallel.

One FlashInfer commit for the family, as the context-parallel packages import their single-rank siblings
(..cake_kda_det_train and ..cake.cake_kda_train; the runtime resolves the second to _vendor.cake_kda_train).
7a13d7ba rather than c5db8d3c: it carries FlashInfer #8629, a correctness fix of the det single-rank package (with
the in-kernel q / k L2 norm, its replay backward over a batched arena, KdaDetArena(batch_tokens=...), read up to 63
rows of the next document group before they were written), and the other changes of these packages since c5db8d3c:
#8552, #8620, #8625, #8630 and #8652 (performance; #8625 runs the fused L2 norm's GLM intra stage with another
program on SM103a, #8620 sums the fast packages' raw-gate parameter gradients in FP64 on SM100a / SM103a). The entry
points of this repository pass no arena, so #8629 does not change their results. The later commits up to FlashInfer
main change none of the four directories.

cake_kda_train/cake_backend.py keeps two FlashInfer hooks, listed under allow: it reads CAKE_KDA_CKPT_HANDOFF
(the checkpoint hand-off override of its chunk_kda), and _jit_nvcc_version imports flashinfer.jit.cpp_ext inside
its body to name the toolkit of the libraries; the kda implementation rebinds that function to the runtime loader's
nvcc when it is imported.

Vendored with, from the root of this checkout next to a FlashInfer clone at ../flashinfer:

    python tools/vendor_cake.py fi --checkout ../flashinfer --commit 7a13d7ba9b75c9bb9c02452df36e3f34417b5d57 \
        --op kda \
        --package flashinfer/experimental/cake_kda_det_train \
        --package flashinfer/experimental/cake_kda_det_train_kcp \
        --package flashinfer/experimental/cake/cake_kda_train \
        --package flashinfer/experimental/cake_kda_train_kcp \
        --allow-env-read cake_kda_train:cake_backend.py \
        --allow-flashinfer-import cake_kda_train:cake_backend.py:_jit_nvcc_version

Co-Authored-By: Claude <noreply@anthropic.com>
FlashInfer c5db8d3ccd3fa3842812280f7f208718dc0b8610: the SiLU-gated RMSNorm training packages, deterministic
(cake_rmsnorm_silu_gate_det_train_backend, 14 files) and non-deterministic (cake_rmsnorm_silu_gate_train_backend,
30 files), each one level deep under miles_ext_kernels/cake/_vendor/, and their block ops.gated_rmsnorm in
_vendor/VENDORED.json. Written from a FlashInfer checkout by:

    python tools/vendor_cake.py fi --checkout ../flashinfer --commit c5db8d3ccd3fa3842812280f7f208718dc0b8610 \
        --op gated_rmsnorm --package flashinfer/cake_rmsnorm_silu_gate_det_train_backend \
        --package flashinfer/cake_rmsnorm_silu_gate_train_backend

Co-Authored-By: Claude <noreply@anthropic.com>
Vendor FlashInfer's Cake RMSNorm training packages of flashinfer-ai/flashinfer
eb3eb68d7deebbe619e05d93ecfd9796e4eaf2e3 into miles_ext_kernels/cake/_vendor/, byte for byte, as the packages of the
op rmsnorm in _vendor/VENDORED.json:

- cake_rmsnorm_det_train_backend (flashinfer/cake_rmsnorm_det_train_backend, 582 files): the deterministic forward
  and backward.
- cake_rmsnorm_train_backend (flashinfer/cake_rmsnorm_train_backend, 581 files): the backward with a non-deterministic
  weight gradient in some token-count windows; it runs its deterministic sibling for deterministic=True, so the two
  packages move together.

eb3eb68d includes flashinfer-ai/flashinfer#8512: on SM100a and SM103a the deterministic backward of the smallest token
counts sums each weight-gradient column in a fixed row order, so its bits no longer depend on the SM count. The files
and their blobs are the ones the previous rmsnorm package vendored from the same commit.

    python tools/vendor_cake.py fi --checkout ../flashinfer --commit eb3eb68d7deebbe619e05d93ecfd9796e4eaf2e3 \
        --op rmsnorm --package flashinfer/cake_rmsnorm_det_train_backend \
        --package flashinfer/cake_rmsnorm_train_backend

Co-Authored-By: Claude <noreply@anthropic.com>
Vendor the DSA kernel packages into miles_ext_kernels/cake/_vendor/ with
tools/vendor_cake.py, byte for byte, and record them under ops.dsa of
VENDORED.json:

- cake_dsa_train: FlashInfer's DSA training kernels for 64 local query
  heads (the fast variant: dKV and dk_rope accumulate in FP32 with
  red.global).
- cake_dsa_indexer_det: FlashInfer's lightning-indexer scores and
  deterministic exact top-k.
- cake_dsa_sparse_det: the DSA det version, the generated sparse
  attention forward / backward and lightning-indexer logits / backward
  kernels of radixark/miles#3599 (86 sources and registry.json; no
  floating-point atomics, key-side gradients reduced from FP32 partials
  in a fixed 4-way tree). The tool writes the TVM-FFI host surface in
  place of the upstream torch-extension one and a build.json generated
  from the registry, so these kernels build with nvcc and load with
  tvm-ffi through the same loader as the FlashInfer packages; SM107a
  compiles the SM103a records.

Both FlashInfer packages come from c5db8d3, the commit these kernels
were validated at: cake_dsa_train is byte for byte the same at 7a13d7ba,
and cake_dsa_indexer_det differs there only by a later performance
change (flashinfer-ai/flashinfer#8691).

Commands (run from the repository root, with a FlashInfer checkout at
../flashinfer and a miles checkout at ../miles):

    python tools/vendor_cake.py fi --checkout ../flashinfer \
        --commit c5db8d3ccd3fa3842812280f7f208718dc0b8610 --op dsa \
        --package flashinfer/experimental/cake_dsa_train \
        --package flashinfer/experimental/cake_dsa_indexer_det
    python tools/vendor_cake.py miles-pr --checkout ../miles \
        --pr 3599 --commit a1b763c82360afeb00c551aeb7ea93b30870e5bf \
        --op dsa --name cake_dsa_sparse_det \
        --path miles/kernels/attention/dsa/cake \
        --include csrc --include registry.json \
        --exclude csrc/cake_host_shim.h --arch-from sm_107a=sm_103a

Co-Authored-By: Claude <noreply@anthropic.com>
The KDA op of the cake namespace: miles_ext_kernels/cake/kda.py declares it (OpSpec, selected by --kda-backend
cake), miles_ext_kernels/cake/_impl/kda/ implements it on the four vendored packages, OPS gains "kda" and the
README its Ops and Prebuild rows. The entry points are those of the kda family before this layout: chunk_kda,
chunk_kda_fwd, chunk_kda_bwd, and partition_kcp, build_context_kcp, chunk_kda_kcp, chunk_kda_fwd_kcp,
chunk_kda_bwd_kcp, reduce_gate_param_grads_kcp. Each call reads config.variant("kda") when it runs: det unless
configured.

Variants. det (cake_kda_det_train, cake_kda_det_train_kcp) repeats bit for bit. fast (cake_kda_train,
cake_kda_train_kcp, deterministic=False) runs the S1 gate-gradient programs on some backward rows of the activated
gate mode and, in the raw gate mode on SM100a / SM103a, reduces the gate parameters' gradients in FP64 in no fixed
order. fast has no in-kernel q / k L2 norm: such a call runs det and logs it once (config.warn_runs_det). Every
miles call takes that L2 norm, so cli_variants is ("det",): --cake-kda-variant is registered hidden, validate_args
writes det back for every process, and a fast choice from miles, which would change no program, is refused by
argparse. Direct callers configure either variant. A backward reads its saved set's package (a saved set is read by
the package that wrote it); saved=None replays the forward in the package of the call. Every call hands its package
use_qk_l2norm_in_kernel as given, so the backward of a fast saved set asked for the L2 norm, a mode its forward did
not run, is the fast package's ValueError rather than a gradient without the norm. Partitions and contexts hold no
variant.

Operands as miles hands them over. beta may be FP32 (miles gives flash-linear-attention the FP32 sigmoid) and v a
column slice of the packed projection: chunk_kda* round beta to BF16 and make v dense before the packages' checks,
inside the autograd graph, and return dbeta in beta's dtype. Before, both raised ValueError in the packages.

Prebuild. prebuild(num_heads=...) checks 1 <= num_heads <= 64 (an int, not a bool) before anything else and then
builds every library of the four packages for the device's architecture (188 / 186 / 189 on SM100a / SM103a /
SM107a): the record a launch takes depends on its token and document counts, its modes, its path and, under context
parallelism, the rank's shard, so no smaller set holds for every call of a layer. This replaces the family's
route_libraries probe, which planned a fixed list of problems and dropped every problem it could not plan (an
unknown gate mode gave an empty list, not an error). release() drops the packages' per-stream entry pools and the
captured graphs of a loaded context-parallel package.

The fast package's _jit_nvcc_version hook (its checkpoint hand-off keys row records on the toolkit) is rebound to
the runtime loader's nvcc when the implementation is imported: flashinfer is never imported. --kda-backend cake
rejects context parallelism (--context-parallel-size > 1) and a layout other than thd at startup, as before.

Co-Authored-By: Claude <noreply@anthropic.com>
The output norm of miles' GDN layers, RMSNorm(x) * weight * SiLU(z) over rows of 128, becomes the op
miles_ext_kernels.cake.gated_rmsnorm: OP declares its two variants, the miles selector
--linear-attention-output-norm-backend cake, its two vendored packages and the entry points rmsnorm_silu_gate and
check_supported; miles_ext_kernels.cake._impl.gated_rmsnorm implements them.

- A call reads the configured variant when its forward runs, and its backward runs the same package. A process that
  never configured the op runs det (it ran fast); validate_args still resolves miles' command-line default to fast,
  and deterministic mode resolves det and rejects an explicit fast. While torch.use_deterministic_algorithms(True)
  is in effect, which the non-deterministic backward refuses, fast runs det and logs it once.
- prebuild(device=None, max_workers=None), reached through cake.prebuild("gated_rmsnorm"), builds every library of
  both packages on the device's architecture, every row-count route of each, so no call compiles afterwards.
  release() has nothing to drop.
- rmsnorm_silu_gate documents when a gate that is a column slice of a wider projection is read in place, and that
  the weight gradient sums over the rows of one call: under tensor parallelism the caller all-reduces it over the
  tensor-parallel group.
- registry.OPS lists gated_rmsnorm; the README's Ops and Prebuild tables list its rows.

Gone with the old package: the module-level VARIANT, the op's own arguments module and configure, the per-call
variant keyword, and SUPPORTED_CAPABILITIES (miles_ext_kernels.cake.ARCH_BY_CAPABILITY names the same devices).

Co-Authored-By: Claude <noreply@anthropic.com>
miles_ext_kernels.cake.rmsnorm declares the op: selected by --linear-attention-input-norm cake, variants det and fast
(--cake-rmsnorm-variant), the two vendored packages. miles_ext_kernels.cake._impl.rmsnorm implements it:
ZeroCenteredRMSNorm, zero_centered_rmsnorm, check_supported, supported_hidden_sizes, prebuild(hidden_size=...) and
release. The op is appended to OPS and to the README's Ops and Prebuild tables.

Changes from the previous rmsnorm package:

- A call reads the configured variant when it runs (det in a process that never configured the op), and its backward
  runs the variant of its forward. The module no longer reads or pins a variant at construction; its variant=
  argument is gone, configure is the one switch.
- Constructing the module compiles nothing. prebuild("rmsnorm", hidden_size=H) builds every library of both variants
  for H (forward and backward, every token-count window), so a variant configured after a module was built runs at
  its next call without a build.
- The weight is allocated where torch's factory functions allocate (a with torch.device("meta") block,
  torch.set_default_device), else on the current CUDA device; device="meta" builds without touching CUDA. The device is
  checked when it is a CUDA device; a hidden size the kernels do not serve is refused on any device.
- reset_parameters() zeroes the weight, also after to_empty().
- sequence_parallel= (default None) is set on the weight when given, at construction and by reset_parameters, as
  Transformer Engine's norms set it, so Megatron all-reduces its gradient across the tensor-parallel ranks under
  sequence parallelism. None leaves the attribute to the caller: reset_parameters keeps a mark set on the weight.
- An unsupported device raises NotImplementedError (was ValueError), as the runtime's device_arch does.
- The package's configure, prebuild(hidden, device, variant=) and SUPPORTED_CAPABILITIES are replaced by
  miles_ext_kernels.configure, miles_ext_kernels.cake.prebuild("rmsnorm", hidden_size=...) and
  miles_ext_kernels.cake.ARCH_BY_CAPABILITY.

fast still runs det, logged once, under torch.use_deterministic_algorithms(True), which the non-deterministic package
refuses.

On SM107a, hidden size 4096 launches two programs above the 232448 B shared-memory opt-in (271360 B, CUDA 13.4's
oversized shared-memory mode): det's backward reduce above 4096 tokens and its copy in fast's backward from 4097 to
24064 tokens. A process whose CUDA runtime is older than 13.4, such as the 13.0 runtime wheel a cu130 torch installs,
rejects that mode at the launch ("cudaFuncSetAttribute failed: invalid argument"), and the error it leaves pending
fails the next CUDA call too. The op now checks the loaded runtime (libcudart of the toolkit's major) and the
driver's oversized ceiling for every program of the width that sets the mode, at construction on a CUDA device,
check_supported, prebuild and the first call of a width on a device, and raises RuntimeError naming the programs,
the runtime and the remedy before any launch. Other widths and SM100a / SM103a launch no such program. The README's
Ops and Prebuild rows state the limit.

Co-Authored-By: Claude <noreply@anthropic.com>
Add the DSA op: miles_ext_kernels/cake/dsa.py (its OpSpec, variants det
and fast, --cake-dsa-bwd-workspace-bytes and --cake-dsa-lazy-build) and
its implementation under miles_ext_kernels/cake/_impl/dsa/:

- ops.py: sparse_attention, the lightning indexer, the det forward and
  backward entry points, prebuild and release. The variant is read when
  a call runs; a call the fast variant does not serve (other than 64
  heads, a sink, another d_qk) runs det and logs it once per reason.
- native.py: the DSA det version (cake_dsa_sparse_det) built and loaded
  through the runtime loader, one library per stage and architecture.
  Every argument binds by its exported name; a TMA source binds as a
  tensor whose descriptor the stage's launcher encodes on the host.
- kernels.py, reduction.py, layouts.py, indexer_topk.py: the det host
  side, the fixed-order reduction, the layout adapters and the det
  indexer top-k (cake_dsa_indexer_det).

Register the op in OPS and add its rows to the README's Ops and
Prebuild tables.

Behaviour, compared with the previous layout:

- sm_scale is required: the d_qk ** -0.5 default is not the scale of
  the absorbed MLA these kernels serve.
- A call takes no variant argument; configure() selects it.
- A batch-local bshd index outside its batch element's keys is an
  empty slot; it no longer reads the next element's keys. So is an
  indexer index past the keys.
- --cake-dsa-bwd-workspace-bytes bounds the det backward's partials
  and the index scratch of their sort (one sort of every chunk when it
  fits, else one per chunk); the chunking, and with it the det bits,
  follows from it.
- The fast backward receives the packed segment count (num_segments;
  the batch for bshd) to plan its key passes; the bits do not change.
- A call without query rows returns empty outputs and zero gradients,
  forward and backward, attention and indexer (and the indexer's top-k
  selection, with or without key rows).
- The indexer serves 8, 16, 32 or 64 heads of dimension 128 in its
  logits, its backward and the selected scores alike; 24 is rejected
  before any launch.
- A selected key with a -inf score (outside its query's window) gets
  no gradient, even a NaN one: the backward sees it as an empty slot.

Co-Authored-By: Claude <noreply@anthropic.com>
miles_ext_kernels/cake/lm_head.py declares the op (torch-free): variants
det and fast, selected by --log-probs-backend cake, the packages
cake_lm_head_det_loss and cake_lm_head_loss, the entry point log_probs and
the option --cake-lm-head-chunk-size (default 4096). Its validate hook
keeps the startup checks of the previous arguments module: the chunk size
range, Megatron, TP 1, the policy and SFT losses, no entropy, BF16, no
LoRA on the output layer, no MuP, H a multiple of 512 and V of 256.

miles_ext_kernels/cake/_impl/lm_head/ reads the variant and the chunk size
when a call runs (the vendored autograd function keeps the forward's
package for the backward). prebuild(*, device, max_workers) builds every
stage log_probs can launch on the device's architecture (every stage of
both packages except their candidate-support support_* stages, which
serve other entries), with and without a temperature; release() clears
the binding caches (and their workspaces) of both packages. registry.OPS
and the README Ops and Prebuild tables get their lm_head lines.

Review items:

- prebuild(temperature=False) left the four temperature stages
  (gemm_logits_*_tau, row_finalize_tau) to the first temperature call:
  prebuild now always builds them; lm_head takes no model parameters.
- A label >= V gave a finite, plausible logp: every call now queues the
  device-side assertion max(labels) < V ahead of the kernels (no host
  synchronization). Every negative label, not only -100, marks an ignored
  row; the docstrings say so.
- On SM103a with a CUDA 13.0 toolkit every forward waits for the device,
  also with num_valid_rows: the packages keep those calls on their
  host-count path (their mitigation of a ptxas 13.0 fault). It is
  documented, and the first call or prebuild on such a device logs it
  once; any other toolkit (12.9, 13.1 or newer) avoids it.
- dW is BF16 only: it stays so. The log-probability entries of both
  packages return a BF16 dW, and the autograd engine casts a gradient to
  its input's dtype, so an FP32 option for a BF16 W would change nothing.
  The docstring states the FP32 [V, H] accumulator and the memory the
  backward holds.
- V must be a multiple of 256, the kernels' logits tile: a vocabulary such
  as 151,936 is rejected at startup with that reason (padding it would
  change the softmax), and documented.

Behavior changes against the previous lm_head package:

- A process that never configured the op runs det (was fast); miles' CLI
  still resolves an unset --cake-lm-head-variant to fast, and to det under
  --deterministic-mode.
- fast no longer turns into det while torch.use_deterministic_algorithms
  is on: the variant comes from configure alone, where deterministic mode
  rejects fast.
- log_probs takes no variant= or chunk_size= argument; device_arch,
  prebuild and the supported capabilities come from miles_ext_kernels.cake,
  and an unsupported GPU raises NotImplementedError (was RuntimeError).
- An X off CUDA raises ValueError (was RuntimeError).

Co-Authored-By: Claude <noreply@anthropic.com>
tests/cake/kda/: the kda family's tests on the cake layout, with per-tensor accuracy budgets.

- test_arguments.py (torch-free): the spec, the hidden --cake-kda-variant (det written back, fast refused), the
  settings validate_args rejects, configure for direct callers, an import without torch.
- test_kda_variant.py (CPU, torch): the package of every entry point under det and fast, the in-kernel L2 norm call
  (logged once), the backward of a saved set of either package (a fast set asked for the in-kernel L2 norm is the
  fast package's ValueError), the beta / v seam and its gradients, the head-count
  check before any package or build runs, the fast toolkit hook, that fast plans programs det does not ship (S1 in
  the activated gate mode on every architecture, R1c in the raw gate mode on SM100a / SM103a), that prebuild builds
  the same 188 / 186 / 189 libraries for every head count, and release().
- test_kda_gpu.py: det in miles' call (q / k / v column slices, raw gate, in-kernel L2 norm) against the exact FP64
  recurrence, every output and gradient within twice the largest error measured (_budgets.py, one B200 and one GB300):
  the token tensors in relative RMS error (a 1.02 scale of out, dq, dk, dv or dbeta fails it), dA_log and ddt_bias
  in error over the root sum of squares of the terms of their exact sums, free of the shape's cancellation;
  bit-identical repeats, packed views and an FP32 beta against contiguous BF16 operands, autograd / saved set /
  replay / checkpoint bits, default and side stream bits, a fresh process's bits, and flash-linear-attention parity
  on every shape (each tensor within twice fla's error on the same inputs).
- test_kda_fast_gpu.py: fast against the same reference within twice det's error on the same inputs (no bit checks:
  fast promises none), its forward / backward split and checkpointed node, saved sets of either package, and the
  in-kernel L2 norm call that runs det giving det's bits.
- test_kda_kcp_gpu.py: the context-parallel entry points on 2 and 4 GPUs, marked multi_gpu, so a one-GPU run
  deselects it (-m "not multi_gpu") instead of reporting 20 skips; with fewer GPUs than a group needs it skips with
  that count.
- test_prebuild_gpu.py: ROUTES over every call-time switch (gate and L2 norm modes, packed views and FP32 beta,
  explicit forward and backward, replay, both checkpoints, the segmented route with saved states, 1 to 64 heads,
  more than 256 documents, a token sweep with empty documents, the context-parallel entry points on a one-rank
  partition with host cu_seqlens), checked after one prebuild with no build allowed.

The elementwise gate the family used (atol = rtol = 1e-2 on every tensor) let a 2 % error of out, dv or dbeta pass;
the relative RMS budgets do not, and the elementwise gate is not carried over. dg is exempt from the 1.02 detection
check, as its own error is about 1 %.

dA_log and ddt_bias are sums over every token of a head. Their relative error is set by how far the terms cancel,
which varies with the shape (on one B200 and one GB300, 0.8 % to 29 % for dA_log and 4.1 % to 6.3 % for ddt_bias),
and dA_log has one value per head, so a relative budget per shape is loose at one head. Over the root sum of squares
of the terms (_reference.gate_param_terms) the error is 0.6 % to 3.3 % for dA_log and 4.7 % to 6.3 % for ddt_bias,
its largest at one head; fla's on the same inputs is 1.2 % to 2.8 % and 4.6 % to 5.6 % (the fla parity rows print
both). These budgets are twice the largest.

Co-Authored-By: Claude <noreply@anthropic.com>
tests/cake/gated_rmsnorm/:

- test_arguments.py (CPU): the declaration and its variant flag; an unconfigured process and an unconfigured call
  run det; miles' resolution (fast by default, det in deterministic mode, explicit values kept, fast rejected in
  deterministic mode, the flag rejected without the backend); the variant read at every call and det under torch's
  deterministic algorithms, with the packages replaced by recorders; check_supported's rejections before any device
  query; import and configuration without torch.
- inputs.py: miles' GDN layout. The gate is a column slice of the fused [q | k | v | z | b | a] projection, whose
  width (2 * Hk * 128 + 2 * Hv * 128 + 2 * Hv, padded to a multiple of 8) is the gate's token stride: the kernels
  read the gate in place when it is a multiple of 16 elements and copy it otherwise. Every forward + backward
  checks the documented result types: y BF16 of x's shape, every gradient BF16, dw of shape [128].
- test_gated_rmsnorm_gpu.py: every value-head count (12, 16, 32, 48) with the gate read in place and copied, the
  path asserted, among them 16 key / 32 value heads (width 12352, in place) and its twin with 8 more columns (12360,
  copied), at 1 to 32771 tokens: y, dx and dz within one BF16 ulp of FP64 and dw within det's bound, every gate
  failing a 2 % error; det bitwise three times in one process and once in a new process; a contiguous gate the same
  bits; fast's y, dx and dz det's bits. Also calls alternating between the default and a side stream,
  checkpointing, accumulation, a tensor-parallel head split (each rank's dw covers its own heads, their sum is the
  layer's) and the toolkit check.
- test_prebuild_gpu.py: ROUTES (both gate paths at every head count, a 2-D input, a token sweep that reaches every
  row-count module of the non-deterministic package, checked against the device's SM count, a weight without a
  gradient, torch's deterministic algorithms) under check_prebuild; prebuild's libraries and idempotence; configure
  after a call switches the next call without a build; no flashinfer module is imported.

Co-Authored-By: Claude <noreply@anthropic.com>
tests/cake/rmsnorm/:

- test_arguments.py (torch-free): registration, variant resolution with and without miles' deterministic mode,
  rejections, configure, det in an unconfigured process.
- test_module.py (CPU and meta tensors, the kernels replaced by a recorder): construction on the meta device,
  reset_parameters after to_empty, the sequence_parallel attribute (none by default; set from the argument; a mark the
  caller set kept by reset_parameters) and Megatron's selection rule, refusal of widths the kernels do not serve,
  construction without a build, the variant each call reads, fast running det under torch's deterministic algorithms,
  the arguments of prebuild (its refusal without a CUDA device in a child process that sees none, so it runs on GPU
  nodes too); the programs that launch in the oversized shared-memory mode (SM107a at 4096 only) and
  the refusal of their width, with the CUDA runtime and driver facts replaced (runtime 13.0, none loaded, no oversized
  ceiling, a ceiling too small).
- test_rmsnorm_gpu.py: accuracy against FP64 at FlashInfer's budgets; det bit for bit three times in one process,
  once in a fresh process, at the smallest token counts and on a side stream (hidden sizes 4096 and 5120); fast's y and
  dx against det; strided rows, checkpointing, no tokens, a module built on the meta device, the toolkit check, a
  backward that runs its forward's variant. A width this process cannot launch (SM107a at 4096 without a CUDA 13.4
  runtime) is refused instead of run (_refusal.py); the refusal holds exactly where a fresh process with the check
  disabled fails to launch the width, and with the runtime taken as 13.0 SM107a refuses 4096 at check_supported,
  prebuild, construction and the first call, while every other width and architecture runs.
- test_prebuild_gpu.py: ROUTES (every token-count window of both packages, [T, H] input, strided rows, FP32 weight, no
  tokens, checkpointing, torch's deterministic algorithms) for every width after one prebuild, with no build allowed
  (prebuild of a width this process cannot launch raises its refusal);
  a variant configured after construction runs without a build; no flashinfer module is imported.
- test_sequence_parallel_gpu.py (multi_gpu, 2 GPUs): under tensor and sequence parallelism the all-reduce rule selects
  the weight, and the ranks keep one bit-identical weight that follows the single-GPU run.

Co-Authored-By: Claude <noreply@anthropic.com>
Add tests/cake/dsa/:

- test_arguments.py, test_package.py, test_dispatch.py and
  test_indexer_topk.py (CPU): the arguments and configuration, the
  three packages and the CUDA 13.4 floor of SM107a whatever a package
  records, the stage selection, the argument binding of every det
  stage (TMA sources included), the calls fast serves, the required
  sm_scale, the segment count, the index ranges, the det backward's
  memory plan, calls without query rows (the indexer forward and
  backward and its top-k selection included), the indexer head counts
  and the gradient mask of the selected scores.
- test_dsa_gpu.py and test_indexer_topk_gpu.py: accuracy against FP32
  and FP64 references at every head count from 4 to 128, det bits
  repeated in one process, in a fresh process, on a side stream and on
  a second GPU, the det backward within its workspace budget, and the
  reviewed limits on a GPU (batch-local and packed indices outside the
  keys are empty slots). The two-GPU tests are marked multi_gpu.
- test_prebuild_gpu.py: ROUTES over every call-time path (each head
  count from 4 to 128, 24 included; RoPE and NoPE; with and without a
  sink; thd, bshd and sbhd; no query rows; the indexer and its det
  top-k), each group checked after one prebuild.

Co-Authored-By: Claude <noreply@anthropic.com>
tests/cake/lm_head/:

- test_arguments.py (CPU): the flags through miles' hooks, variant
  resolution with and without deterministic mode, the startup rejections
  (151,936 with the logits-tile reason) and the process configuration.
- test_package.py (CPU torch): prebuild asks for every stage of both
  packages with and without a temperature on each architecture; only
  SM103a programs built by CUDA 13.0 take the host-count path, logged
  once; the program registries read the toolkit through the loader;
  release clears the binding caches and imports nothing.
- test_lm_head_gpu.py: the accuracy gate against an FP32 oracle (checked
  to fail a 2 % error), det bit for bit in one process and in a new one,
  fast's det bits, empty calls, frozen inputs, given counts, zero-gradient
  rows, the configured variant read at call time, a side stream, a second
  GPU (multi_gpu), a given count that does not wait for the device behind
  a queued sleep (and the SM103a CUDA 13.0 exception), negative labels, a
  label >= V ending the stream in a fresh process, dW as the BF16 cast of
  the FP32 accumulator, and the geometry and device errors.
- test_prebuild_gpu.py: ROUTES over temperature off, scalar, per-row and
  1.0, one row to several chunks, chunk sizes 1024 and 65535, given counts,
  ignored and zero-gradient rows, frozen inputs and no-grad forwards at two
  geometries; after one prebuild none compiles, for either variant.

Co-Authored-By: Claude <noreply@anthropic.com>
Vendor FlashInfer's Cake package flashinfer/cake_gated_act_mxfp8_backend
(fused SwiGLU + MXFP8 quantization, forward and backward) of
flashinfer-ai/flashinfer c5db8d3ccd3fa3842812280f7f208718dc0b8610 into
miles_ext_kernels/cake/_vendor/cake_gated_act_mxfp8_backend/, byte for
byte (131 files, package tree ffec0b8; the tree is the same at
FlashInfer 7a13d7ba9b75c9bb9c02452df36e3f34417b5d57), and record it as
ops.gated_act_mxfp8 in _vendor/VENDORED.json, from the repository root with
a FlashInfer checkout at ../flashinfer:

    python tools/vendor_cake.py fi --checkout ../flashinfer \
        --commit c5db8d3ccd3fa3842812280f7f208718dc0b8610 \
        --op gated_act_mxfp8 \
        --package flashinfer/cake_gated_act_mxfp8_backend

Its Python modules read no environment variable and import flashinfer
only in the pinned cake_build.py loader, so the entry has no allow list.
build.json (Cake revision ce6037e6) lists 24 libraries for sm_100a, 28
for sm_103a and 23 for sm_107a (min_cuda 12.8, 12.9 and 13.4).

The op module registers the package in the next commit.

Co-Authored-By: Claude <noreply@anthropic.com>
miles_ext_kernels.cake.gated_act_mxfp8 runs the vendored Cake fused SwiGLU
+ MXFP8 quantization (cake_gated_act_mxfp8_backend), forward and backward,
on SM100a, SM103a and SM107a. The forward quantizes silu(gate) * up of a
BF16 [M, 2K] gate / up input (M and K positive multiples of 128), the
backward the gate and up gradients [M, 2K] of a BF16 grad_output [M, K]:
E4M3 values with RCEIL E8M0 scales per 32 elements of a row and / or a
column, the scales in the swizzled 128 x 4 layout of the MXFP8 GEMM
operands.

OP: one variant, det. A call runs one program of its route chosen by
M * K alone, and every program of a route writes the same bytes, so the
outputs are a function of the inputs and repeat bit for bit. No miles
argument selects the op, so it registers no flag; it has no option. The
exports are the package's own entry points, silu_and_mul_mxfp8_quantize
and silu_and_mul_mxfp8_quantize_backward.

_impl.gated_act_mxfp8 installs the runtime loader on the package.
prebuild(device=None, max_workers=None) takes no model parameter (M and K
are runtime arguments) and builds every library of the device's
architecture: the six routes (forward and backward; rowwise, colwise or
both), each with every program its M * K rule picks. An unsupported
device raises NotImplementedError, a toolkit that cannot build the
architecture RuntimeError (SM107a needs CUDA 13.4). release() has nothing
to free: no workspace, plan or graph, and the loaded libraries stay
loaded, since a captured CUDA graph may still launch them.

registry.OPS gains the op; the README lists it in the Ops and Prebuild
tables.

Co-Authored-By: Claude <noreply@anthropic.com>
FlashInfer c5db8d3ccd3fa3842812280f7f208718dc0b8610: the dense projection GEMM training packages, deterministic
(dense_projection_det_gemm, 190 files) and non-deterministic (dense_projection_gemm, 30 files), each one level deep
under miles_ext_kernels/cake/_vendor/, and their block ops.dense_gemm in _vendor/VENDORED.json. Both come from one
commit: the non-deterministic package runs its deterministic rows on its sibling. Written from a FlashInfer checkout
by:

    python tools/vendor_cake.py fi --checkout ../flashinfer --commit c5db8d3ccd3fa3842812280f7f208718dc0b8610 \
        --op dense_gemm --package flashinfer/experimental/dense_projection_det_gemm \
        --package flashinfer/experimental/dense_projection_gemm

Co-Authored-By: Claude <noreply@anthropic.com>
… not the README

The README no longer lists ops in a table; the op module and implementation
docstrings already state that neither variant is batch-invariant.
@Zhichenzzz
Zhichenzzz merged commit 3f726c6 into radixark:main Nov 5, 2026
4 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.

3 participants