Skip to content

Kimi K3: fused decode pack and retained-buffer compaction behind a decode_fusion plan setting - #23

Open
tunamitom wants to merge 2 commits into
voipmonitor:release/k31-exl3-tp8-20261108from
tunamitom:pr/flare-pr8-fused-decode-pack-lane
Open

tunamitom wants to merge 2 commits into
voipmonitor:release/k31-exl3-tp8-20261108from
tunamitom:pr/flare-pr8-fused-decode-pack-lane

Conversation

@tunamitom

Copy link
Copy Markdown

Two decode-graph reductions, each exact by construction, each with its
own measured node delta (two separate deltas that happen to be equal —
not one number counted twice):

  • retained-buffer compaction (always on): selected_local_records
    takes out=/count_out= so the compaction kernel writes the DSA
    stage's retained physical/counts buffers directly (one write per
    element instead of two; the per-stage copy disappears). Its own delta:
    −48 graph nodes per decode round = the two per-stage copy nodes
    removed × 24 decode stages. Default None allocates exactly as before.
    Bytes beyond each row's count are -1 and don't care.
  • fused pack (--decode-fusion, default off): one compiled Triton
    kernel replaces the 8-kernel decode score/ID pack, writing pairs
    straight into the existing cursor buffer (select/copy/integer-only,
    exact by construction, allocates nothing). Its own delta: a separate
    −48 graph nodes per decode round = the shipped Inductor-fused pack
    chain's 3 graph nodes per call fused to 1, × 24 decode stages.

Together the two deltas remove 96 nodes/round (0.100–0.112 ms/round at a
measured 1.042–1.163 µs dispatch cost per removed node; see Evidence).

Design notes

  • No environment channel: the gate is the plan setting
    --decode-fusion -> Plan.decode_fusion (recorded in plan settings)
    -> PreparationConfig -> PreparedDecoder/PreparedMLA/PreparedDSA
    -> the PreparedDistributedTopK constructor argument. The component
    never reads the environment in any form and no preparation stamps a
    variable; a regression test pins the source free of environment reads.
    (That regression is a source-text check; its docstring and assert
    messages now say exactly what it checks and that it is not a
    behavioural proof.)
  • Aliasing guard fixed: it compared untyped_storage().data_ptr(),
    so disjoint GraphBufferPool cursor buffers (one shared chunk, disjoint
    byte ranges) raised ValueError on every eager pool caller. The guard
    now checks byte ranges (strided bounding box) and rejects only real
    overlap of out=/count_out= with the kernel's inputs; it stays out
    of Dynamo traces (torch.compiler.is_compiling() skip, as before).
    Tests cover true overlap (raises), disjoint shared-chunk pool buffers
    (pass), and GPU equality of the retained write vs the allocating path.
    Design origin, disclosed for the AGENTS.md authorship rule: the
    byte-range guard design (data_ptr() + strided bounding-box extent)
    came from an external design-review comment (Claude Opus 5.5's
    first-pass review of this work), not from the original implementation
    pass. The guard is input validation around the kernel — not kernel
    code and not the aliasing mechanism itself — and is stated neutrally so
    the maintainer can judge.
  • Bit-exactness GPU-verified: 5-case GPU matrix (rows 1-8, widths
    48-2048, worlds 8/16, tied / NaN / -inf scores, dead entries, ids whose
    int32 narrowing wraps) — fused pack vs shipped pack bit-equal 5/5 on
    an idle GPU; the test rides with the branch.

Follow-up

  • rebuild_manifest.from_plan does not record decode_fusion, so a
    fused package's rebuild manifest describes the 8-kernel pack. Suggest
    recording it when set (absent ⇒ false), the same treatment as the
    block-verification manifest key. The output is bit-exact either way,
    so this is a manifest-accuracy follow-up, not a correctness fix.

Numerics class

BIT-EXACT — select/copy/integer-only paths; GPU-verified as above. The
compaction writes the bytes the previous copy produced.

Evidence, stated honestly

The shipped pack chain is already Inductor-fused, so the fused pack's
saving is its own node reduction (3 nodes/call → 1, × 24 stages = −48/round),
not eight sequential launches; the compaction's −48/round is separate
(its two per-stage copy nodes × 24). Node counts come from the exported
package's captured decode-round graphs; the per-node dispatch cost
(1.042–1.163 µs) and the resulting 0.100–0.112 ms/round for the two
combined (−96 nodes/round) are from captured-chain replays of those
graph shapes. None of it was re-measured on this tree; no before/after
decode round time or tok/s is claimed. The maintainer may drop the gated
fused pack and keep the compaction alone (the commits are split
accordingly), or ask for a serving-level measurement first. The new
Triton kernel is offered for kernel-level review. Authorship: written by
Hermes Agent (hermes-agent); no restricted model authored the kernel
code.

Tests

  • tests/components/test_distributed_topk_pack.py — 11 passed
    (3 compaction/guard incl. 1 GPU, 2 CPU pack parity, 5 GPU bit-equality
    cases, 1 environment-read source-text check). Re-run on the final tip: CPU-only
    5 passed / 6 skipped (CUDA cases skip without a GPU); with one idle GPU the full
    suite re-ran on the final tip: 11 passed (CUDA matrix included).
  • tests/recipes/test_kimi31.py + tests/recipes/test_kimi3_preparation.py
    — 49 passed (identical to base; the preparation test plan stub now
    carries decode_fusion). Final-tip re-run (all three suites, CPU):
    54 passed, 6 skipped; base-failure parity re-run: base's 49 passed,
    zero failures at base and none new at the tip.
  • Full run (tests/cli + tests/recipes + tests/components): exactly
    the 13 pre-existing failures of the intake-lane base — no new
    failures (base-failure parity).
  • Commit 1 standalone: 52 passed, imports OK (bisection-clean).
  • ruff: touched files clean; zero new findings vs base.

Dependencies

None on the flashinfer PRs (pure flare-side graph work).

selected_local_records takes out=/count_out= so the compaction kernel
writes the DSA stage's retained physical/counts buffers directly: one
write per element instead of two (the per-stage copy into
self.physical/self.counts disappears; about -48 graph nodes per decode
round). Default None allocates exactly as before, so every existing
caller is unchanged. Bytes beyond each row's count are -1 and stay don't
care -- consumers read exactly [0, count) records and nothing past them.

The retained buffers come from the graph buffer pool: disjoint cursor
views carved from one shared chunk. The aliasing guard therefore checks
byte ranges, never whole storages, and rejects only real overlap of out=
or count_out= with the kernel's inputs (undefined for out= aliasing an
input of the same kernel). A storage-level comparison raised ValueError
on legitimate pool callers with disjoint buffers; the guard is exact for
contiguous tensors and covers the strided bounding box otherwise. The
guard runs eagerly only: Dynamo cannot trace data-pointer comparisons,
and at capture time the callers pass the prepared, statically distinct
retained buffers (the checks are skipped while compiling; kernel and
captured behaviour unchanged).

Authorship: written by Hermes Agent (hermes-agent) for tunamitom; no
restricted model (Claude Opus 5.5 or otherwise) authored this kernel code.

Validation: tests/components/test_distributed_topk_pack.py 3 passed on
this tree, including on an idle GPU (the Triton kernel writing retained
pool buffers equals the allocating path bit for bit; true overlap is
rejected on CPU; disjoint shared-chunk pool buffers pass the guard).
tests/recipes/test_kimi31.py + tests/recipes/test_kimi3_preparation.py
49 passed, identical to the release-lane base (49 passed). Material
limitations: no full-model serve was run; the graph-node count is from
the captured decode rounds of the served deployment, not re-measured
here. The compaction path is exact by construction (same kernel, same
arithmetic, direct write of the same values the copy produced).

Numerics class: BIT-EXACT. The retained-buffer write produces the bytes
the previous copy produced; the guard change alters no kernel behaviour.

Design-Signed-Off-By: Hermes Agent (hermes-agent); design-review=a38f
The decode score/ID pack dispatches eight small kernels per call (ge,
where, copy, cast, mul, add, where, copy). This replaces them, when
requested, with one compiled Triton kernel that reads indices, scores and
the rank and writes the pairs straight into the operator's existing
cursor buffer: select, copy and integer arithmetic only, exact by
construction, and it allocates nothing (no process-local temporary, no
registry). Default off keeps the shipped 8-kernel pack bit for bit.

The gate is a plan setting, not an environment channel: --decode-fusion
is recorded in the plan settings (Plan.decode_fusion) and threaded from
the preparation config through PreparedDecoder, PreparedMLA and
PreparedDSA to the PreparedDistributedTopK constructor argument. The
component reads no environment variable to choose captured behaviour in
any form, and no preparation stamps one; a regression test pins the
component free of environment reads (a source-text check of the module,
stated as such in the test). The setting rejects non-Kimi recipes with a
usage error, and the test plan stub carries the new field (the same stub
gap that broke the preparation suite in earlier work is covered).

Commit shape: this is the second of two standalone commits; the
retained-buffer compaction and its capture-safe aliasing guard landed
first and stand alone. Each commit builds, imports and passes its tests
independently: commit 1 alone ran its 52 tests green on this tree, both
commits together 60. No CUDA-graph capture of a Kimi decode round is
re-run on this tree (limitation below), so no capture claim is made.

Bit-exactness (GPU-verified): the fused pack equals the shipped pack
bit for bit (int32-view comparison, so dead entries' id -1 -- whose FP32
view is NaN -- compares equal) over a 5-case GPU matrix: rows 1-8,
widths 48-2048, worlds 8 and 16, tied scores, NaN and -inf scores, dead
entries, and ids whose int32 narrowing wraps (index 2^28). Run on an
idle GPU in this session: 5/5 bit-equal. The CPU body equals the same
reference including ties and dead entries.

Benefit, stated honestly: the shipped pack chain is already
Inductor-fused (3 graph nodes per call in the exported decode-round
graphs), so this commit's saving is its own node reduction: those 3
nodes per call become 1, x 24 decode stages per round = -48 nodes/round
for the fused pack alone. That -48 is this commit's delta and is
separate from the compaction's own -48 (its two per-stage copy nodes
x 24); the two are equal only because both remove two nodes per call at
24 stages, and together they remove 96 nodes/round = 0.100-0.112
ms/round at a measured dispatch cost of 1.042-1.163 us per removed node
(captured-chain replays; not re-measured on this tree). Not eight
sequential kernel launches. The 0.100-0.112 ms/round figure is not a
before/after decode round time or tok/s claim. If the maintainer prefers,
the gate can be dropped entirely and the fused pack resubmitted with a
serving-level measurement.

Authorship: written by Hermes Agent (hermes-agent) for tunamitom; no
restricted model (Claude Opus 5.5 or otherwise) authored this kernel
code. The new Triton kernel is offered for kernel-level review.

Validation: tests/components/test_distributed_topk_pack.py 11 passed on
this tree (3 compaction + guard tests incl. one GPU test, 2 CPU pack
parity tests, 5 GPU bit-equality cases, 1 environment-read regression),
and tests/recipes/test_kimi31.py + test_kimi3_preparation.py 49
passed (25 + 24).
Full suite runs (tests/cli + tests/recipes + tests/components) show
exactly the 13 pre-existing failures of the release-lane base -- no new
failures. Material limitations: no full-model serve and no decode-round
timing were run here; the Dynamo fullgraph capture smoke is not re-run
on this tree (the aliasing guards skip while compiling via
torch.compiler.is_compiling(), so no data-pointer comparison enters a
trace); the CPU bodies exercise the exact arithmetic but the kernel
itself only runs on GPU.

Numerics class: BIT-EXACT -- select/copy/integer-only path; GPU-verified
against the shipped pack as above.

Design-Signed-Off-By: Hermes Agent (hermes-agent); design-review=a38f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant