Repository navigation
Conversation
ATOM_GDN_SSM_DTYPE=fp16, the agentic recipe's default, halves the KDA recurrent state pool, but mono refused it: K1 read and wrote the recurrent state in place as fp32, so the recipe forced the pool to fp32 at C1. K1 now takes the pool's dtype as a build constant (state16). Every state it loads (a request's state, or under ReplaySSM its checkpoint) is widened to fp32, the recurrence runs in fp32 as before, and every state it stores (a spec verify's or plain decode's snapshot slots, or under ReplaySSM the checkpoint on a flush) is rounded to fp16, as the production Triton kernels load and store it. An fp32 pool compiles to the same instructions as before; bf16 is still refused. The runner reads the dtype off every mono layer's pool at bind, like the conv window, so layers that disagree refuse the whole runner, and checks it against the build on every step. The recipe's C1 launch no longer forces the pool to fp32. CPU tests pin the config gate (fp32 and fp16 admitted, bf16 refused with its reason), that an all-fp16 pool set reads as fp16, the dtype in every K1 key, the refusal of layers that disagree on it, and that a step takes exactly the pool its build was made for. Signed-off-by: Rebecca Lee <rebecca.lee@amd.com>
Contributor
🏷️ CI GuideRuns automatically on every eligible PR before approval:
Heavy model tests:
|
valar-cop
reviewed
Nov 3, 2026
valar-cop
left a comment
There was a problem hiding this comment.
some changes needed..
e2e or accuracy of mono on fp16 pool with ReplaySSM on, and at DCP>1?
ci green.
| # fp16; any other pool would be reinterpreted rather than converted | ||
| ( | ||
| envs.ATOM_GDN_SSM_DTYPE == "fp32", | ||
| envs.ATOM_GDN_SSM_DTYPE in ("fp32", "fp16"), |
There was a problem hiding this comment.
with mono on by default this flips mesh cells to mono without measuring them, please pin ATOM_MONO_ENABLE=0 there or measure one..
| # Both overrides are C1-only, because mono is C1-only. They are | ||
| # preconditions, not tuning: without them mono refuses at build time and | ||
| # C1 silently serves the paged path, which is what `ATOM_MONO_ENABLE=1` | ||
| # The override is C1-only, because mono is C1-only. It is a |
There was a problem hiding this comment.
this removes the only doc of ATOM_GDN_SSM_DTYPE, add a row in docs/environment_variables.md?
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ATOM_GDN_SSM_DTYPE=fp16halves Kimi-K3's KDA recurrent state pool, and it is the agenticrecipe's default in every band. Mono decode refused it, because K1 read and wrote the state in
place as fp32, so the recipe's C1 launch (the band that runs mono) sets the pool back to fp32 as a
precondition. This PR has K1 take the pool's dtype as a build constant (
state16): every state K1loads is widened to fp32, the recurrence runs in fp32 as before, and every state it stores is
rounded to fp16, which is how the production Triton kernels treat the same pool. An fp32 pool
builds the same instructions as before; bf16 is still refused. The recipe's C1 launch drops the
override.
On 8x MI355X with the recipe's C1 launch, this gives the worst rank 45% more KV blocks (7158 to
10407) with no measurable change in the median decode step.
Motivation
The fp32 precondition doubles the state pool wherever mono is armed, and keeps mono out of any
deployment that runs the pool in fp16, as the agentic recipe does by default. The production
path already serves this state in fp16 in every band.
Changes
kda.py:KdaPreBuild.state16, a build-key field (kernel names gain_h0/_h1).state_at(c, slot): one address function for every state access (a request's initial state,each token's snapshot slot, and under ReplaySSM the checkpoint load and the flush).
state_loadswidens each state to fp32 andstore_statesrounds it back to fp16,to nearest even (
v_cvt_pk_f16_f32).runner.py:_state_dtype()requires one recurrent dtype across every mono layer, as_state_len()doesfor the conv window; it goes into every K1 key.
_k1checks each step's pool against the build: a pool of the other dtype would be read atthe wrong element size.
_recurrent_state()refuses anything but fp32 or fp16, which would be reinterpreted ratherthan converted.
config.py:config_refusaladmits fp16 as well as fp32.recipes/Agentic-Kimi-K3.md: the C1 launch no longer setsATOM_GDN_SSM_DTYPE=fp32.Performance
8x MI355X, the recipe's C1 launch on main, against the same launch with this PR (the fp32
override gone):
The fp16 state pool is half the size, so with the same weights the worst rank fits 45% more KV
blocks. The median decode step does not measurably change: median ITL is 0.15 ms (1%) longer in
both waves, within the step's run-to-run variation, which K2 dominates (K2, which this PR does
not touch, is 131 us faster in this PR's trace, and moves as much between launches of the same
build). K1, the only kernel this PR changes, is 21 us (0.9%) faster per forward; its time varies
by under 10 us between steps and launches, so that gain is likely real.
The extra blocks are capacity, not speed: at 128 tokens a block, the worst rank's pool grows from
916k to 1.33M tokens, and no request in this benchmark needs more than 72 blocks.
Method
rocm/atom-dev:nightly_202611011508-rocm10.0with ATOM main606aceb11mounted over theimage's, with and without this commit. Every arm preloads the image's
/usr/lib/x86_64-linux-gnu/libgomp.so.1: main's weight loader (feat(mono): shared DSA indexer mono launch; Kimi-K3 mono to 64 rows, plain decode and ReplaySSM #2871) looks upomp_set_num_threadsamong the process's global symbols, this image's torch loads libgomp withlocal scope, and without the preload no rank loads.
recipes/Agentic-Kimi-K3.md's C1 launch: TP8, DCP1, DSpark with 7 speculative tokens, syntheticacceptance length 3.84, mono decode on, the five dense projections excluded from
ptpc_fp8;ATOM_GDN_SSM_DTYPE=fp32on main, the default fp16 with this PR.atom.benchmarks.benchmark_serving, random dataset, input 8192 and output 1024 tokens withrange ratio 0.8,
--ignore-eos, 5 prompts at concurrency 1, seed 0: a warmup wave, then twotimed waves. With speculative decoding a request gets one streamed chunk per step, so the median
ITL is the decode step period.
peak_torch) and KV blocks are from the server logs. K1 and K2 times are from a 3 storch-profiler window 20 s into a third wave: medians over the target forward's graph replays
in rank 0's trace (165 and 167).
Limitations
rounded to fp16, as production's is. GSM8K (below) is the accuracy check.
flush are checked only by the fp32-twin probe below, not by a recipe band.
kl2-normalized,betain (0, 1) and a decay of at most 1, a token adds at mostv_j^2to the squared norm ofvalue channel
j's state column.)Testing
gfx950 (MI355X),
rocm/atom-dev:nightly_202611011508-rocm10.0.test_kimi_k3_mono_state_pools.py,test_kimi_k3_mono_config_refusal.pyand
test_kimi_k3_mono_mla_k2.py: the config gate admits fp32 and fp16 and refuses bf16; anall-fp16 pool set reads as fp16; every K1 key carries the dtype; layers that disagree are
refused; a step refuses a pool of the other dtype.
none skipped. The image's site-packages
testspackage (fromcufile_python) shadows ATOM'sand was masked; unmasked, 2 tests fail, on main too.
rows, spec verify, plain decode and ReplaySSM) are main's instructions; each of the 17 fp16 K1
builds has its fp32 build's VGPR, SGPR, spill and LDS counts.
the same build with
state16=Falseruns on copies of everything K1 writes, from the poolwidened exactly. The core, prefix, block bank, conv window and ReplaySSM records must be
bit-equal, and the fp16 pool must equal the twin's fp32 pool rounded to fp16. 81,600 of 81,600
launches pass (150 steps x 68 KDA layers x 8 ranks) in each of three runs: spec verify, plain
decode, and spec verify with ReplaySSM forced on (78,880 of its launches flushed state).
questions right on one side only (McNemar p = 0.75); main with mono off scores 0.9613. 1319
questions, 5-shot chat, greedy, no synthetic acceptance.
blackandruffclean.