-
Notifications
You must be signed in to change notification settings - Fork 2.6k
[None][perf] Avoid paged MSA K/V materialization during prefill #16857
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
peihu-nv
wants to merge
10
commits into
NVIDIA:main
Choose a base branch
from
peihu-nv:peihengh/m3-strided-paged-kv-main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
334db27
[None][perf] Avoid paged MSA K/V materialization during prefill
peihu-nv 411708f
Merge branch 'main' into peihengh/m3-strided-paged-kv-main
peihu-nv 5a38f2c
use fetch_content for patching
tburt-nv f494551
Merge branch 'main' into peihengh/m3-strided-paged-kv-main
peihu-nv 235f552
[None][fix] Consolidate MSA wheel packaging
peihu-nv 722bfd6
[None][fix] Address MSA packaging review feedback
peihu-nv ddeee3a
Merge remote-tracking branch 'upstream/main' into peihengh/m3-strided…
peihu-nv 429e4fd
[None][fix] Harden MSA packaging validation
peihu-nv 392b5b4
[None][build] Reuse TRT-LLM CUTLASS for MSA
peihu-nv 39f01c0
[None][fix] Address MSA packaging follow-ups
peihu-nv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
This file was deleted.
Oops, something went wrong.
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
Submodule MSA
deleted from
e2ebe7
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,225 @@ | ||
| diff --git a/python/fmha_sm100/cute/interface.py b/python/fmha_sm100/cute/interface.py | ||
| index d72b17a..eca5b8c 100644 | ||
| --- a/python/fmha_sm100/cute/interface.py | ||
| +++ b/python/fmha_sm100/cute/interface.py | ||
| @@ -136,6 +136,35 @@ def _prepare_paged_kv_for_tma(k, v, blk_kv: int): | ||
| return k, v | ||
|
|
||
|
|
||
| +def _prepare_paged_hnd_input(tensor: torch.Tensor, blk_kv: int) -> torch.Tensor: | ||
| + """Keep TMA-compatible paged HND views strided across physical pages. | ||
| + | ||
| + The sparse prefill kernel imports the runtime tensor strides through | ||
| + DLPack. It requires each ``[page_size, head_dim]`` head plane to be packed, | ||
| + but the outer physical-page stride may include other coalesced cache roles. | ||
| + Materialize every other layout to preserve the public API's old contract. | ||
| + """ | ||
| + if tensor.ndim != 4: | ||
| + return tensor.contiguous() | ||
| + if int(tensor.shape[2]) != int(blk_kv): | ||
| + return tensor | ||
| + | ||
| + head_dim = int(tensor.shape[3]) | ||
| + page_size = int(tensor.shape[2]) | ||
| + packed_within_page = ( | ||
| + tensor.stride(3) == 1 | ||
| + and tensor.stride(2) == head_dim | ||
| + and tensor.stride(1) == page_size * head_dim | ||
| + ) | ||
| + alignment_bytes = 16 | ||
| + aligned_for_tma = ( | ||
| + tensor.data_ptr() % alignment_bytes == 0 | ||
| + and tensor.stride(0) >= 0 | ||
| + and tensor.stride(0) * tensor.element_size() % alignment_bytes == 0 | ||
|
peihu-nv marked this conversation as resolved.
|
||
| + ) | ||
| + return tensor if packed_within_page and aligned_for_tma else tensor.contiguous() | ||
| + | ||
| + | ||
| def _validate_cu_seqlens( | ||
| cu_seqlens: torch.Tensor, | ||
| *, | ||
| @@ -736,10 +765,21 @@ def sparse_atten_func( | ||
| max_seqlen_q = int(max_seqlen_q) | ||
| max_seqlen_k = int(max_seqlen_k) | ||
|
|
||
| + k_input = ( | ||
| + k.contiguous() | ||
| + if page_table is None | ||
| + else _prepare_paged_hnd_input(k, blk_kv) | ||
| + ) | ||
| + v_input = ( | ||
| + v.contiguous() | ||
| + if page_table is None | ||
| + else _prepare_paged_hnd_input(v, blk_kv) | ||
| + ) | ||
| + | ||
| return _sparse_atten_csr_varlen_forward( | ||
| q.contiguous(), | ||
| - k.contiguous(), | ||
| - v.contiguous(), | ||
| + k_input, | ||
| + v_input, | ||
| k2q_row_ptr.contiguous(), | ||
| k2q_q_indices.contiguous(), | ||
| int(topK), | ||
| diff --git a/python/fmha_sm100/cute/test_sparse_atten.py b/python/fmha_sm100/cute/test_sparse_atten.py | ||
| index 21c777e..b5f078b 100644 | ||
| --- a/python/fmha_sm100/cute/test_sparse_atten.py | ||
| +++ b/python/fmha_sm100/cute/test_sparse_atten.py | ||
| @@ -61,6 +61,81 @@ DECODE_DIM = 128 | ||
| DECODE_KV_TOKEN_SWEEP = tuple(2**exp for exp in range(3, 21)) | ||
|
|
||
|
|
||
| +def test_prepare_paged_hnd_input_keeps_aligned_outer_page_stride() -> None: | ||
| + pages, roles, heads, page_size, head_dim = 5, 3, 2, 128, 128 | ||
| + pool = torch.empty( | ||
| + pages, | ||
| + roles, | ||
| + heads, | ||
| + page_size, | ||
| + head_dim, | ||
| + dtype=torch.float8_e4m3fn, | ||
| + device="cuda", | ||
| + ) | ||
| + view = pool[:, 1] | ||
| + | ||
| + assert not view.is_contiguous() | ||
| + prepared = sparse_interface._prepare_paged_hnd_input(view, page_size) | ||
| + assert prepared.data_ptr() == view.data_ptr() | ||
| + assert prepared.stride() == view.stride() | ||
| + | ||
| + | ||
| +def test_prepare_paged_hnd_input_materializes_unpacked_tokens() -> None: | ||
| + pages, heads, page_size, head_dim = 5, 2, 128, 128 | ||
| + storage = torch.empty( | ||
| + pages, | ||
| + heads, | ||
| + page_size * 2, | ||
| + head_dim, | ||
| + dtype=torch.float8_e4m3fn, | ||
| + device="cuda", | ||
| + ) | ||
| + view = storage[:, :, ::2, :] | ||
| + | ||
| + prepared = sparse_interface._prepare_paged_hnd_input(view, page_size) | ||
| + assert prepared.is_contiguous() | ||
| + assert prepared.data_ptr() != view.data_ptr() | ||
| + torch.testing.assert_close(prepared, view, rtol=0, atol=0) | ||
| + | ||
| + | ||
| +def test_prepare_paged_hnd_input_materializes_unaligned_outer_stride() -> None: | ||
| + pages, heads, page_size, head_dim = 5, 2, 128, 128 | ||
| + outer_stride = heads * page_size * head_dim + 1 | ||
| + storage = torch.empty( | ||
| + pages * outer_stride, | ||
| + dtype=torch.float8_e4m3fn, | ||
| + device="cuda", | ||
| + ) | ||
| + view = storage.as_strided( | ||
| + (pages, heads, page_size, head_dim), | ||
| + (outer_stride, page_size * head_dim, head_dim, 1), | ||
| + ) | ||
| + | ||
| + prepared = sparse_interface._prepare_paged_hnd_input(view, page_size) | ||
| + assert prepared.is_contiguous() | ||
| + assert prepared.data_ptr() != view.data_ptr() | ||
| + torch.testing.assert_close(prepared, view, rtol=0, atol=0) | ||
| + | ||
| + | ||
| +def test_prepare_paged_hnd_input_defers_page_size_validation() -> None: | ||
| + pages, roles, heads, page_size, head_dim = 5, 2, 2, 128, 128 | ||
| + pool = torch.empty( | ||
| + pages, | ||
| + roles, | ||
| + heads, | ||
| + page_size, | ||
| + head_dim, | ||
| + dtype=torch.float8_e4m3fn, | ||
| + device="cuda", | ||
| + ) | ||
| + view = pool[:, 0] | ||
| + | ||
| + prepared = sparse_interface._prepare_paged_hnd_input(view, page_size // 2) | ||
| + assert prepared.data_ptr() == view.data_ptr() | ||
| + with pytest.raises(ValueError, match="page_size == blk_kv"): | ||
| + sparse_interface._prepare_paged_kv_for_tma(prepared, prepared, page_size // 2) | ||
| + | ||
| + | ||
| @contextmanager | ||
| def _nvtx_range(message: str): | ||
| torch.cuda.nvtx.range_push(message) | ||
| @@ -1786,6 +1861,74 @@ def test_sparse_page_atten( | ||
|
|
||
| _assert_forward_close(out, out_ref, out_pt.float(), lse, lse_ref) | ||
|
|
||
| + | ||
| +def test_sparse_page_atten_strided_outer_page_matches_packed() -> None: | ||
| + inputs = _build_paged_inputs( | ||
| + batch=1, | ||
| + seqlen_q=2048, | ||
| + seqlen_kv=2048, | ||
| + head_kv=2, | ||
| + qhead_per_kv=16, | ||
| + dim=128, | ||
| + topk=16, | ||
| + blk_kv=128, | ||
| + causal=True, | ||
| + page_size=128, | ||
| + seqused_trim=0, | ||
| + dtype=torch.float8_e4m3fn, | ||
| + ) | ||
| + k_packed = inputs["k_paged"].detach().clone() | ||
| + v_packed = inputs["v_paged"].detach().clone() | ||
| + pool = torch.empty( | ||
| + k_packed.shape[0], | ||
| + 4, | ||
| + *k_packed.shape[1:], | ||
| + dtype=k_packed.dtype, | ||
| + device=k_packed.device, | ||
| + ) | ||
| + k_strided = pool[:, 1] | ||
| + v_strided = pool[:, 3] | ||
| + k_strided.copy_(k_packed) | ||
| + v_strided.copy_(v_packed) | ||
| + | ||
| + assert not k_strided.is_contiguous() | ||
| + assert not v_strided.is_contiguous() | ||
| + assert ( | ||
| + sparse_interface._prepare_paged_hnd_input(k_strided, inputs["blk_kv"]).data_ptr() | ||
| + == k_strided.data_ptr() | ||
| + ) | ||
| + assert ( | ||
| + sparse_interface._prepare_paged_hnd_input(v_strided, inputs["blk_kv"]).data_ptr() | ||
| + == v_strided.data_ptr() | ||
| + ) | ||
| + | ||
| + def run(k: torch.Tensor, v: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: | ||
| + return sparse_atten_func( | ||
| + inputs["q"], | ||
| + k, | ||
| + v, | ||
| + inputs["k2q_row_ptr"], | ||
| + inputs["k2q_q_indices"], | ||
| + 16, | ||
| + blk_kv=inputs["blk_kv"], | ||
| + causal=True, | ||
| + softmax_scale=inputs["softmax_scale"], | ||
| + return_softmax_lse=True, | ||
| + cu_seqlens_q=inputs["cu_seqlens_q"], | ||
| + cu_seqlens_k=inputs["cu_seqlens_k"], | ||
| + max_seqlen_q=inputs["max_seqlen_q"], | ||
| + max_seqlen_k=inputs["max_seqlen_k"], | ||
| + page_table=inputs["page_table"], | ||
| + seqused_k=inputs["seqused_k"], | ||
| + schedule=inputs["schedule"], | ||
| + ) | ||
| + | ||
| + packed_out, packed_lse = run(k_packed, v_packed) | ||
| + strided_out, strided_lse = run(k_strided, v_strided) | ||
| + torch.testing.assert_close(strided_out, packed_out, rtol=0, atol=0) | ||
| + torch.testing.assert_close(strided_lse, packed_lse, rtol=0, atol=0) | ||
| + | ||
| + | ||
| @pytest.mark.parametrize("paged", [False, True]) | ||
| @pytest.mark.parametrize("causal", [True]) | ||
| @pytest.mark.parametrize("batch", [3]) | ||
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| name: msa | ||
| description: MiniMax Sparse Attention (fmha_sm100) kernels for SM100 sparse attention | ||
| source: submodule | ||
| source: fetched | ||
| directory_matches: | ||
| - 3rdparty/MSA | ||
| - fmha_sm100 |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.