Repository navigation
[unified-memory] Stride the KV translate kernel and route every translate through it (6/7) - #40330
Merged
ch-wan merged 5 commits intoOct 2, 2026
Conversation
caihuali95
requested review from
BBuf,
DarkSharpness,
Fridge003,
HaiShaw,
HydraQYH,
Qiaolin-Yu,
Ying1123,
alphabetc1,
celve,
hanming-lu,
hebiao064,
hnyls2002,
huangtingwei9988,
hzh0425,
ispobock,
kpham-sgl,
merrymercy,
pyc96,
xiezhq-hermann,
yizhang2077 and
yuan-luo
as code owners
September 19, 2026 09:55
This was referenced Sep 19, 2026
This was referenced Sep 20, 2026
caihuali95
force-pushed
the
mainline/consolidate-translate-kv-loc
branch
from
September 24, 2026 02:35
d2eee4f to
2f2efb9
Compare
caihuali95
requested review from
Edwardf0t1,
ch-wan and
fzyzcjy
as code owners
September 24, 2026 02:35
caihuali95
force-pushed
the
mainline/consolidate-translate-kv-loc
branch
2 times, most recently
from
September 25, 2026 05:49
88d5cc0 to
0654006
Compare
ch-wan
force-pushed
the
mainline/consolidate-translate-kv-loc
branch
from
September 28, 2026 19:53
0654006 to
c0cd042
Compare
ch-wan
requested review from
JustinTong0323,
sogalin,
wisclmy0611 and
zijiexia
as code owners
September 28, 2026 19:53
ch-wan
force-pushed
the
mainline/consolidate-translate-kv-loc
branch
from
September 28, 2026 21:32
c0cd042 to
a099985
Compare
ch-wan
force-pushed
the
mainline/consolidate-translate-kv-loc
branch
3 times, most recently
from
October 1, 2026 06:21
1f4e99e to
1faa3af
Compare
`write_loc_to_kernel_ids` addressed `loc` and `out` as `ptr + offs`, so it had
to assert both were contiguous. Walk them as a (rows, cols) view instead, with
the row and column strides passed per launch, and a strided slice addresses
correctly.
The decomposition sits behind a STRIDED constexpr: a contiguous pair still
takes the flat path and pays no div/mod, which is what the per-forward write
translate under cuda-graph capture uses. The strides are runtime arguments, not
constexpr, because a page table's live width changes every few decode steps and
a constexpr stride would recompile with it. Offsets are computed in int64: a row
stride times a row count can pass 2^31.
Nothing feeds this kernel a strided tensor today, so this changes no behaviour
on its own; it is the precondition for folding `translate_kv_loc` onto this
path, where the SWA read path's `page_table[:bs, :max_seq_len]` arrives.
Two details that are not stride arithmetic:
* `out_width` writes a packed lane range and zero-fills the tail past the
batch, which is a flat notion -- lanes past `loc`'s own rectangle would
wrap onto live ids. That mode now asserts a packed `loc` instead of
relying on the blanket assert this commit removes.
* the `out=None` allocation moves off `empty_like`, which would mirror a
strided `loc` and hand the caller a non-contiguous result.
`test_matches_reference_on_a_strided_view` is red before this commit
(`AssertionError: loc must be contiguous`) and green after.
`test_out_width_needs_a_packed_loc` passes both sides -- before, on the blanket
assert; after, on the narrow one -- and pins the branch that would otherwise
regress silently. The Triton addressing itself is reachable only on a GPU, so
it is covered by test_write_loc_to_kernel_ids_strided.py, which compares a
strided view against a contiguous copy of the same values.
…kernel
With the kernel-page multiplier gone, `translate_kv_loc` (several torch ops
over `index_select`) and `translate_kv_loc_for_kernel` (one Triton launch)
computed the same id for any mapped virtual token. Keep the fused one:
`translate_kv_loc` calls it, and the torch body and
`translate_kv_loc_for_kernel` go. The read-table path
(`KVIndexTranslator._translate_full`), the SWA read path and
`UnifiedSWAKVPool.translate_loc_from_full_to_swa` were already on the fused
kernel and see no change; the SWA / Mamba composites' unused delegates are
deleted.
What changes for the callers that were on the torch path -- the composites'
`translate_kv_loc` and the PD / HiCache transfer translates:
* a negative loc (the padding `-1` included) or an id past the table used
to fault -- `index_select` takes neither, and on a GPU it is a
device-side assert. It now resolves to 0, the sink, as the fused path
already did. This runs inside captured graphs, where a fault is not a
diagnostic.
* 2-D, int32 and (after the previous commit) strided inputs are accepted;
the result is int64 and shaped like the input.
* one launch instead of up to six.
Unchanged: every id the torch path accepted translates to the same value,
including the tombstoned-v2p clamp, `out=` identity and data_ptr stability,
and `translate(x, out=x)` aliasing.
…story Name the padding-loc test for what it pins (a `-1` read-table slot resolves to the sink) and drop two tests that pinned nothing new: an int32 2-D page table test that repeats `test_translate_accepts_an_int32_page_table`, and a compaction test that compared the translate against its own formula. The SWA transfer translate's docstring said `translate_loc_from_full_to_swa` takes different ids than the transfer engine hands over; both take virtual ids and translate through the same SWA table.
The fa3 full-CG prefill comment said that re-running full->swa on the rebound out_cache_loc indexes past the swa v2p table and lands on the sink. It doesn't: the swa v2p has one row per full virtual page, and a full physical page number is always below that, so the map would resolve it to an unrelated token's swa slot without any error. Say so, and stop citing that misuse as the scenario of the past-the-table sink test.
The CPU-side sink tests take the torch branch, so this file is the only coverage of the Triton kernel itself, and every lane it built was a page-aligned int64 loc inside the table: a wrong in-page offset, a missing past-the-table guard or a broken int32 load would all have passed. The lanes now include non-zero offsets, a loc below the sentinel and a page past the table, the column-slice and CPU-reference tests run int32 as well as int64, and the CPU-reference test checks that every sink lane resolves to 0.
ch-wan
force-pushed
the
mainline/consolidate-translate-kv-loc
branch
from
October 2, 2026 00:11
1faa3af to
b8927d3
Compare
This was referenced Oct 2, 2026
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.
6/7 — stride the KV translate kernel and route every translate through it
Base: #40329 (5/7). 5 commits, 11 files, +361/−159.
Why
maincomputesv2p[t // ps] * ps + t % psin two places: the fused Triton kernel(
_translate_loc_fused, used by the read table, the SWA read and the write translate) and atorch body (
translate_kv_loc, used by the composites and the PD / HiCache transfertranslates). They disagree on the ids nobody looks at: the torch body faults on a negative or
past-the-table id and on 2-D input, while the kernel sends them to the page-0 sink. The
kernel could not take every caller, because it flat-addresses its input and so needs it
contiguous, while the SWA read path hands down
page_table[:bs, :max_seq_len], a columnslice of the capture-stable buffer.
Commit 1 — walk the kernel by its strides
locandoutare walked as a(rows, cols)view with row and column strides passed perlaunch. The decomposition sits behind a
STRIDEDconstexpr, so a contiguous pair still takesthe flat path with no div/mod; that is what the per-forward write translate under cuda-graph
capture uses.
tl.constexpr. A page table's live widthchanges every few decode steps, and a constexpr stride would recompile with it.
int32 overflow here would mis-address silently.
out_widthstays on the flat path, by assert. That mode writes a packed lane range[0, W)with a zero tail; a non-contiguous 1-Dlocwould send the tail lanes over thelive prefix, so the assert is load-bearing.
out=Noneallocation no longer mirrors a stridedloc, so the caller gets acontiguous result.
Commit 2 — one implementation
translate_kv_loccalls the fused kernel, and the torch body andtranslate_kv_loc_for_kernelare deleted. For the callers that were on the torch path — the composites'
translate_kv_loc,the PD transfer translates, retraction's
get_cpu_copy/load_cpu_copyand_materialize_inflight_write_set(HiCache's transfer translate was already on the kernel) — a negative or past-the-table id now resolves to thepage-0 sink instead of faulting; 2-D, int32 and strided inputs are accepted; and each call is
one launch instead of up to six. Trading a fault for the sink is deliberate: these calls can
run inside a captured graph, where a device-side assert is not a diagnostic. Paths already on
the fused kernel — the read table, the SWA read, the pool's FULL→SWA — do not change, and
every id the torch body accepted translates to the same value.
Tests
test_matches_reference_on_a_strided_viewandtest_translate_accepts_a_strided_page_tablefail on 5/7 (
loc must be contiguous) and pass here.test_a_loc_past_the_sentinel_lands_on_the_sink,test_an_out_of_range_loc_lands_on_the_sinkand
test_padding_loc_lands_on_the_sinkpin the sink semantics.test_out_width_needs_a_packed_locpins the branch that would otherwise regress silently.kernels/ops/memory/test_write_loc_to_kernel_ids_strided.py(GPU) is the only test thatenters the kernel, so it is the one that can catch a wrong
row * rs + col * cs; its oracleis the kernel on a contiguous copy, plus the CPU reference. Its lanes cover non-zero
in-page offsets, the
-1sink, a tombstoned page, a loc below the sentinel and a page pastthe table, in int32 and int64. Dropping the past-the-table guard or the in-page offset from
the kernel fails it.
Validation
A GPU differential of
translate_kv_locagainst the closed-form definition matches in all 114cases: int32 / int64; page sizes 1, 4, 64; 1-D, 2-D, stride-2, column-slice, transposed and
empty inputs; tombstoned pages; −1, −2, −(ps + 1) and past-the-table locs;
out=None, packed,column-slice (neighbours untouched) and in-place
out=. The 5/7 implementation agrees on all 50inputs it accepts.
UnifiedSWAKVPool.translate_loc_from_full_to_swais one CUDA launch at 5/7,at this PR and at 7/7.
Unit tests
At this head on the H200, the strided kernel test and
test_unified_mla_views.pypass (24tests), and the kernel test fails when the past-the-table guard or the in-page offset is
removed from the kernel. The stack tip (#40331) was run over the wider unit scope on the H200 and
end to end on B200; see its description.
Follow-ups this PR does not do
--enable-unified-memory. Commit 1 removesone technical reason for it, but validating the lift needs a local-attention model.
UnifiedMambaPool.translateis still a plain torch gather; atpage size 1 it computes the same value but does not clamp, so a tombstoned row returns
-1rather than the sink.
CI States
Latest PR Test (Base): ⏳ Run #36944767639
Latest PR Test (Extra): ⏳ Run #36944767374
Latest PR Test (AMD ROCm 10): ⏳ Run #36944767640