Repository navigation
[Test] mla_gluon: regression test for the >2GB KV cache path - #4488
Closed
JohnQin-llm wants to merge 1 commit into
Closed
JohnQin-llm wants to merge 1 commit into
JohnQin-llm wants to merge 1 commit into
Conversation
Contributor
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a GPU memory fault in the Gluon MLA kernel by preventing int32 overflow in KV-cache row offset arithmetic when the KV cache exceeds ~2GB, ensuring correct address generation for large KV caches.
Changes:
- Moves KV offset computation inside the
WITHIN_2GBbranches to preserve the existing fast-path behavior. - Widens the KV row index to
int64in theWITHIN_2GB == Falsepath sorow * stridedoes not wrap in int32 for large caches. - Applies the same widening pattern across K_nope (slice 0/1) and optional K_pe loads in both prologue and loop/epilogue sections.
Suppressed comments (3)
aiter/ops/triton/gluon/mla_gluon.py:467
- global_load_to_shared in the >2GB branch is missing the same split_kv_end mask used on the buffer_load_to_shared path. For partial tiles this can read K_pe from invalid rows (or from garbage kv_loc_pe if the page-table load was masked).
offs_k_pe = kv_loc_pe.to(gl.int64)[None, :] * stride_k_pe_bs + offs_d_kpe_1[:, None] + KV_PE_OFFSET
gl.amd.cdna4.async_copy.global_load_to_shared(bufs_kpe.index(0), K_pe_cache + offs_k_pe)
aiter/ops/triton/gluon/mla_gluon.py:488
- global_load_to_shared in the >2GB branch omits the split_kv_end mask that exists on the buffer_load_to_shared path. When the slice-1 half-tile straddles split_kv_end, this can issue out-of-bounds reads.
offs_k_c1 = kv_loc1.to(gl.int64)[None, :] * stride_kv_c_bs + offs_d_ckv_10[:, None]
gl.amd.cdna4.async_copy.global_load_to_shared(bufs_kv1, Kv_c_cache + offs_k_c1)
aiter/ops/triton/gluon/mla_gluon.py:658
- In the epilogue K_pe load, the >2GB global_load_to_shared path is missing the split_kv_end mask that the buffer_load_to_shared path uses. This can read past the valid KV range for partial tiles.
# legalizes). buffer_load needs int32, so only this branch.
offs_k_pe = kv_loc_pe.to(gl.int64)[None, :] * stride_k_pe_bs + offs_d_kpe_1[:, None] + KV_PE_OFFSET
gl.amd.cdna4.async_copy.global_load_to_shared(bufs_kpe.index(async_idx), K_pe_cache + offs_k_pe)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
JohnQin-llm
force-pushed
the
fix/mla-gluon-kv-offset-int32-overflow
branch
from
August 2, 2026 01:17
a8b1fd0 to
0d46701
Compare
JohnQin-llm
marked this pull request as draft
August 2, 2026 01:18
JohnQin-llm
marked this pull request as ready for review
August 2, 2026 01:41
JohnQin-llm
force-pushed
the
fix/mla-gluon-kv-offset-int32-overflow
branch
2 times, most recently
from
August 2, 2026 01:50
bf08068 to
b36b38e
Compare
KV addresses are built as kv_loc * stride_kv_c_bs + offs_d with both operands
int32, so for a 576-wide bf16 row the product wraps once a page id passes
2**31 // 576 = 3,728,270 and the load lands below the cache base:
Memory access fault by GPU node-N ... Reason: Unknown
The existing cases in test_mla.py already compile and run the WITHIN_2GB=False
branch -- kv_max_sz = 65536*32 = 2,097,152 rows is 2.25 GiB, over the 2 GB
cutoff -- but page ids top out at 2,097,151, a factor of 1.78 below the wrap
point, so the branch cannot trigger the overflow. --sequential_page_indices,
which is commented "byte offset can exceed 2^32", reaches ~2,107,152 rows at
batch 256 x ctx 8192 and is still 1.77x short.
Add a case with a 4.72 GiB cache (4,400,000 rows) indexed at row 4,255,488,
covering both a BLOCK_N-aligned length and a partial trailing tile.
The launch runs in a subprocess: a GPU memory fault aborts the interpreter
rather than raising, so an in-process test would SIGABRT and take out the
remaining files in the CI shard instead of reporting one failure. Verified in
both directions on 8x MI355X -- with the fix the child exits 0, on unfixed main
the child exits -6 and the parent reports a clean AssertionError and continues.
Gated on gfx950 and on torch.cuda.mem_get_info() with a 1.3x margin, following
the convention in test_batch_prefill.py.
Depends on ROCm#4474: this test fails on main until that fix merges.
Signed-off-by: Yanyuan Qin <yanyuan.qin@amd.com>
JohnQin-llm
force-pushed
the
fix/mla-gluon-kv-offset-int32-overflow
branch
from
August 2, 2026 02:00
b36b38e to
81e524c
Compare
Closed
1 task done
3 of 13 tasks
Contributor
Author
|
Closed as test coverage is not perf blocker and no review for a while |
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
Add deterministic regression coverage for the greater-than-2-GiB Gluon MLA
KV-cache offset overflow fixed by #4474. This PR is test-only and depends on
#4474; it no longer carries a competing runtime fix.
The test allocates a 4.72-GiB BF16 cache and indexes row 4,255,488, beyond the
signed 32-bit wrap point for a 576-element row. It covers both a full
BLOCK_N=128tile and a partial 100-token trailing tile.Why existing coverage missed it
The existing harness allocates 2,097,152 rows, so it selects the
WITHIN_2GB=Falsebranch but cannot index past the wrap point:2**31 // 576The child process is intentional. An HSA memory fault aborts the interpreter;
subprocess isolation turns that into one actionable test failure instead of
terminating the remaining CI shard. The parent also checks that sufficient
memory is available before launching the 4.72-GiB case.
Public-image reproduction
Hardware: AMD Instinct MI355X (
gfx950). Public base image:vllm/vllm-openai-rocm:kimi-k3@sha256:5aa7e626ff73672f5ca7aae46754570488c23d33ca1ac90756a1d2d1a3fe099b.Test PR head:
81e524ccb559731d5bf9ba98e62ea49ddd80b816.Runtime dependency #4474 head:
1e4135b1070153135de288af87258f94351fdaba.Reconstruct only the two explicit PR deltas; do not merge a moving
main:Save this as
Dockerfile.repro:Build and run only the isolated child case:
Observed on MI355X with #4474: both context lengths passed and the child exited
zero. On the unfixed source, the child exits via SIGABRT after the HSA memory
fault. The parent disables core dumps before starting that intentionally unsafe
control, and the large allocation runs only for the default direct invocation,
not for each of
aiter_test.sh's five argument sweeps. Black, Ruff, andgit diff --checkpassed.Dependency and duplication
#4474 owns the runtime fix. This PR contributes only the missing deterministic
boundary regression and should merge after, or together with, #4474. OpenAI
Codex assisted with testing and review; the author reviewed every changed line
and the final evidence.