[TRTLLM-35237][feat] Tune cute dsl paged MQA logits decode kernel - #14133
Conversation
…M100 Replace DeepGEMM-based indexer logits with a CuTE DSL kernel on SM100+, gated by `use_cute_dsl_logits` config flag. Includes kernel implementation, PyTorch custom op registration, config plumbing, and unit tests. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Replace stale development script name with the actual module filename. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
- Use current_stream() instead of creating a new stream to avoid data races - Remove unnecessary torch.cuda.synchronize() - Add is_sm_100f() check in custom op to fail fast on unsupported GPUs - Rename FP8MQALogitsDGFullKKernel to FP8MQALogitsKernel - Add __all__ to paged_mqa_logits __init__.py - Fix test skip logic to precisely match SM 100/103 only Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…s enabled Broaden the cute_dsl_custom_ops import guard to also trigger when use_cute_dsl_logits is True, so the custom op is registered even when use_cute_dsl_topk is False. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…_logits Align config field name with the op name cute_dsl_fp8_paged_mqa_logits for clarity. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
The custom op already validates SM 100/103 via is_sm_100f(). Also replace getattr with direct attribute access to match use_cute_dsl_topk style. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…ser-facing strings Remove DeepGEMM and DG-FullK references from module docstring, class docstring, function docstrings, print statements, and argparse descriptions. Inline implementation comments retained as design cross-references. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Replace from_dlpack + mark_compact_shape_dynamic compile pattern with make_fake_compact_tensor + TVM FFI in CuteDSLPagedMQALogitsRunner. This eliminates real tensor data at compile time, removes per-call dlpack wrapping and manual CUDA stream passing at runtime. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…pper Add _check_fp8_paged_mqa_logits_dtypes helper that validates all tensor and compute dtypes upfront, collecting all errors into a single ValueError so callers see every mismatch at once rather than one at a time. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
- Add epi_dtype param to PyTorch ref so epilogue matches kernel precision - Add use_int_data mode for fp16 tests to isolate bugs from FP16 rounding - Tighten tolerances: fp32 atol 5e-3→5e-5, fp16 atol 1e-2→1e-3 with rtol - Use torch.testing.assert_close instead of cosine similarity - Add varlen benchmark support for mixed-length serving workloads - Clean up comments and remove stale parametrize line Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…e map Remove broken run_test/parse_args/__main__ block and its helper functions (make_fused_kv, fused_kv_views, _prepare_inputs, _make_dynamic_dlpacks, _get_or_compile_kernel, dsl_fp8_paged_mqa_logits_dg_fullk) from fp8_paged_mqa_logits.py — all had no callers after the missing paged_mqa_logits_helpers module made them non-functional. Accuracy validation is covered by the unit tests. Reuse existing module-level _TORCH_TO_CUTLASS_DTYPE in CuteDSLPagedMQALogitsRunner instead of duplicating it. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…ubtiles Move the FMA unroll granularity check outside the num_epi_subtiles > 1 guard so it also applies when num_epi_subtiles == 1. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Remove is_umma_warp, num_q_stages (local copy), cons_group, num_tma_prod, num_mcast_a/b, and tCtAcc_fake (in __call__) which were assigned but never referenced. Remove unnecessary noqa F401 on pipeline_init_arrive/pipeline_init_wait which are actively used. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…nfig Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…arbitrary next_n Fix cudaErrorMisalignedAddress when num_heads=32 with fp16 output by padding sW stage stride to 128-byte boundary for TMA bulk copy. Update qw_per_stage SMEM budget to use padded stride and actual epi_bytes. In dsa.py, skip MTP batch expansion for DSL kernel (supports arbitrary next_n natively) and use correct scheduler_metadata buffer selection. Expand test coverage with num_heads and fix_length parametrization, pure PyTorch reference, and DG expanded benchmark for next_n=3. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Clamp start_q before reading mContextLens to prevent out-of-bounds access when the scheduler assigns batch_size as a sentinel for CTAs with no work. Move torch.manual_seed into _generate_test_data for reproducibility. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…aged MQA logits Decouple compute_block_kv (always 128) from phys_block_kv (physical page size). When phys_block_kv < 128, the kernel issues num_blocks_per_mma TMA copies per compute tile. Removes the tokens_per_block=128 constraint for the DSL kernel path. Signed-off-by: Mindy Li <lmin@nvidia.com> Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…eduling Hoist shuffle_sync calls before producer_acquire in both TMA warpgroups. The previous ordering placed shuffle after barrier acquire, causing a dependency chain that regressed next_n=3 by 5-7%. SASS now matches baseline exactly. Signed-off-by: Mindy Li <lmin@nvidia.com> Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Signed-off-by: Mindy Li <lmin@nvidia.com> Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…and test Drop dead code flagged in review: - compute_schedule_metadata + cdiv (and now-unused torch import) from the kernel module — never called. - skip_if_unsupported decorator + has_deep_gemm helper + IS_CUTLASS_DSL_AVAILABLE import from the test — defined but never applied. SM100/103 gate already ensures DeepGEMM and CuTe DSL availability. Also refresh the test docstring: the reference is pure PyTorch, not DeepGEMM. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
The DSL variant of test_indexer_decode_with_paged_kv_cache calls torch.ops.trtllm.cute_dsl_fp8_paged_mqa_logits, which raises ValueError on SM != 100/103. Mark only the "dsl" parameter with a skipif so the "deepgemm" backend still runs on Hopper. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…xer_gemm Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com> # Conflicts: # tensorrt_llm/_torch/custom_ops/cute_dsl_custom_ops.py
…xer_gemm Resolve conflicts in DSA paged-MQA-logits dispatch and tests after DeepGEMM submodule bump (4ff3f54d -> c491439e via PR NVIDIA#13340 / DG NVIDIA#304): - dsa.py: take upstream's scheduler_metadata_buffer / _full_next_n selection (mtp3 buffer removed); add DSL early-branch using the existing scheduler_metadata_buffer (built with (num_gen, 1) shape, num_atoms=1, matching DSL's 1-atom-per-q design) and the 1D kv_lens_cuda_runtime slice for context_lens. - dsa.py: introduce module-level _DG_SCHEDULE_BLOCK_KV = 64, used by all 6 get_paged_mqa_logits_metadata calls (3 in on_update_kv_lens(), 3 in Indexer.prepare()) instead of cache tokens_per_block. Decouples schedule SPLIT_KV from cache page size and side-steps a SM100 + block_kv=32 latent regression in DG commit 7f2a703 (NVIDIA#304). - test_dsa_indexer.py: take upstream's scheduler buffer selection; DSL test branch reads scheduler_metadata_buffer + 1D kv_lens. - test_cute_dsl_fp8_paged_mqa_logits.py: 4 metadata calls now pass 2D context_lens via .unsqueeze(-1) and DG_METADATA_BLOCK_KV=64; DG bench drops cluster(2,1,1) for next_n=4 (SM100 always uses num_kv_multicast=1) and passes 2D context_lens to fp8_paged_mqa_logits. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
… MQA next_n coverage
test_dsa_indexer: separate scheduler_metadata_buffer for DSL backend (kNumNextNAtoms=1)
to avoid the next_n>1 alias used by DeepGEMM. Fixes test_indexer_decode_with_paged_kv_cache
across {deepgemm, dsl} x {(4,1),(2,2),(4,3),(4,4)}.
test_cute_dsl_fp8_paged_mqa_logits: extend multi_block next_n coverage to {1,2,3,4};
drop now-obsolete next_n==3 expansion in bench (newer DeepGEMM supports it natively);
update --block_kv default from 128 to 64 to match the new {32,64} assertion in
fp8_paged_mqa_logits.
Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…ress) Snapshot of FP4 MXFP4 block-scaled paged_mqa_logits kernel based on the FP8 kernel: 5 TMA atoms (KV + B + W + SF_KV + SF_Q), per-WG SFA TMEM region, in-place SMEM transpose for UTCCP, per-k_block SFA/SFB slice for cute.gemm. Stage 0 still hangs at tmem.wait_for_alloc bar.sync 1, 320; committing state for further debugging. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
|
PR_Github #49045 [ run ] completed with state |
Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
on_update_kv_lens() previously only refreshed kv_lens_expanded_cuda and scheduler_metadata_buffer_expanded under use_expanded_buffers_for_mtp. With DSL paged-mqa-logits and dsl_expand_factor > 1, overlap scheduler + spec-dec runtime corrections to kv_lens_cuda left both buffers stale, so the kernel scanned a pre-correction KV extent and the schedule referenced pre-correction lengths. Mirror the prepare()-time DSL split block next to the existing MTP branch. Reuse cached (dsl_expand_factor, dsl_atom) — re-running the picker here would let the split drift between prepare and forward and break CUDA graph capture. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
|
/bot run |
|
PR_Github #49206 [ run ] triggered by Bot. Commit: |
|
PR_Github #49168 [ run ] completed with state |
|
PR_Github #49206 [ run ] completed with state |
lfr-0531
left a comment
There was a problem hiding this comment.
The changes in DSA part LGTM~
- _pick_dsl_expand: make num_sms a required argument (drop magic
default 148); both existing callers already pass it via keyword.
- expand_for_dsl field comment: generalize from FP4-only to cover
both FP4 ({1,2,3}) and FP8 ({1,2,3,4}) atom sets.
- FP4 kernel: drop the print(...) fallback notice in
remove_online_sf_transpose; silent fallback is sufficient.
- FP4/FP8 kernels: remove commented-out phys_block_bytes = ... dead
lines.
Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
|
/bot run |
|
PR_Github #49597 [ run ] triggered by Bot. Commit: |
|
PR_Github #49597 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
|
PR_Github #49676 [ run ] triggered by Bot. Commit: |
|
PR_Github #49676 [ run ] completed with state |
PR NVIDIA#14133 updated the FP4 paged-MQA-logits CuTe DSL op to accept non-contiguous kv tensors, so the .contiguous() forcing in this custom op is no longer needed and was triggering an extra copy kernel. Revert to the original reshape-only path. Signed-off-by: tensorrt-cicd <90828364+tensorrt-cicd@users.noreply.github.com>
PR NVIDIA#14133 updated the FP4 paged-MQA-logits CuTe DSL op to accept non-contiguous kv tensors, so the .contiguous() forcing in this custom op is no longer needed and was triggering an extra copy kernel. Revert to the original reshape-only path. Signed-off-by: tensorrt-cicd <90828364+tensorrt-cicd@users.noreply.github.com>
…IDIA#14133) Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…IDIA#14133) Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Summary
_pick_fp4_dsl_expand)with a wave-aware strategy that minimizes scheduler waves and tie-breaks by
max
num_atoms(more SMs busy per wave). Generalize the picker to both FP4and FP8 DSL paths via a
kernel_atomsparameter (FP4:{1,2,3}; FP8:{1,2,3,4}).Motivation
ntask(typical small-B decode), the old picker chosekernel-native
next_n, leaving SMs idle (waves=1 but ntask < 148 on B200).The new picker splits into more atoms so all SMs work in the single wave,
paying
expand_factor xKV HBM re-read cost. On wave-saturated workloads,both pickers converge — the change is a no-op.
FP8 production previously never atom-split (the legacy flag was gated on
use_fp4); the new generalized picker brings FP8 to parity with FP4.Speedup vs legacy picker
performance ut
max_atom / max_naFull performance
FP4 indexer
epi_dtype=float32 output_dtype=bfloat16 num_epi_subtiles=1 mode=fix-len block_kv=64
next_n = 2
next_n = 3
next_n = 4
FP8 indexer
output_dtype=float32 num_epi_subtiles=1 mode=fix-len block_kv=64
next_n = 2
next_n = 3
next_n = 4
Changes
dsa.py_pick_fp4_dsl_expand→_pick_dsl_expand; addsbatch_size,max_ctx,num_sms,kernel_atomsparams; wave-aware decision withlegacy max-atom fallback when batch/ctx unknown.
fp4_prefix):expand_for_dsl,dsl_expand_factor,dsl_atom. Decision is cached at prepare time;forward +
Indexer.prepareread from cache (no per-call re-pick;prepare/forward guaranteed consistent).
max_draft_tokens >= 3to>= 1, sonext_n ∈ {2, 3}can also expand when the picker choosesfactor > 1.metadata.dsl_expand_factor > 1instead ofnext_n > 3.test_cute_dsl_fp{4,8}_paged_mqa_logits.py: bench harness adds anA/B comparison of
maxAtom(baseline) vsmaxNa(new) per row, plusDG/DSL_max_atom,DG/DSL_max_na, andntaskcolumns.test_dsa_indexer.py::test_indexer_decode_with_paged_kv_cache_fp4:adapt to new picker signature and field names; mock expand setup runs
unconditionally for DSL backend so
next_n ∈ {2, 3}paths are exercised.Validation (4.4.2 PyPI wheel, B200)
test_dsa_indexer.py::test_indexer_decode_with_paged_kv_cache_fp4: 8/8 PASSDescription
Test Coverage
PR Checklist
Please review the following before submitting your PR:
PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.
PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.
Test cases are provided for new code paths (see test instructions)
If PR introduces API changes, an appropriate PR label is added - either
api-compatibleorapi-breaking. Forapi-breaking, includeBREAKINGin the PR title.Any new dependencies have been scanned for license and vulnerabilities
CODEOWNERS updated if ownership changes
Documentation updated as needed
Update tava architecture diagram if there is a significant design change in PR.
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.