Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c06c8cd
[None][perf] Port op43 BSX CuTe DSL top-K tiers (direct/reg/tp) onto …
longcheng-nv Jul 26, 2026
666efff
[None][perf] bsx tp: hint-ladder admission fast path (R0 parity on hi…
longcheng-nv Jul 26, 2026
c8fc03a
[None][perf] bsx tp admission: tighten acceptance bound to pivot-band…
longcheng-nv Jul 26, 2026
fb98f75
[None][feat] bsx tiers: MTP (next_n>1) and compress_ratio=1 support; …
longcheng-nv Jul 27, 2026
9535fd9
[None][perf] bsx tp: lean-pivot admission (stage-0c CI override + per…
longcheng-nv Jul 28, 2026
22b7af1
[None][perf] bsx dispatch: measured fallback band table to the in-tre…
longcheng-nv Jul 28, 2026
78013ba
[None][perf] bsx dispatch: recalibrate 131072 fallback band lower bou…
longcheng-nv Jul 29, 2026
e0401dd
[None][test] bsx suite: cut CI wall-clock 46% by deduplicating JIT co…
longcheng-nv Jul 29, 2026
2a1950c
[None][chore] bsx: scrub internal development codenames from comments
longcheng-nv Jul 29, 2026
4126897
[None][chore] GVR decode: resolve the review follow-ups from #16457
longcheng-nv Jul 29, 2026
b82f6e0
[None][fix] apply ruff-format to the follow-up changes
longcheng-nv Jul 29, 2026
4642d92
[None][fix] GVR decode: exact tie-aware terminal for boundary plateaus
longcheng-nv Jul 29, 2026
cd6c106
[None][perf] bsx dispatch: scope the bs=8 fallback band to true 128K …
longcheng-nv Jul 29, 2026
e382f98
[None][fix] gvr top-k: plateau terminal on the register-resident admi…
longcheng-nv Jul 30, 2026
d29db18
[None][fix] bsx review round: order_row accept-and-ignore, device-der…
longcheng-nv Aug 11, 2026
fd97411
[None][chore] conftest: explicit optional type for ref_vals_cache (RU…
longcheng-nv Aug 11, 2026
abee8b8
[None][chore] bsx: memoize cluster-cap verdict, doc fallback-bands kn…
longcheng-nv Aug 11, 2026
4e4abbd
[None][chore] drop the BSX working name: the tiers are GVR optimizati…
longcheng-nv Aug 11, 2026
9dffd71
[None][chore] apply pre-commit formatting missed in the rename commit
longcheng-nv Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 49 additions & 81 deletions tensorrt_llm/_torch/custom_ops/cute_dsl_custom_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -7271,6 +7271,10 @@ def warmup_cute_dsl_radix_topk_decode(
# ------------------------------------------------------------------ #
from ..cute_dsl_kernels.blackwell.top_k.gvr_topk_decode import \
GvrTopKKernel as _GvrTopKKernel
from ..cute_dsl_kernels.blackwell.top_k.gvr_topk_decode_dispatch import \
is_tiered_topk_supported as _is_tiered_topk_supported
from ..cute_dsl_kernels.blackwell.top_k.gvr_topk_decode_dispatch import \
tiered_topk as _tiered_topk

class CuteDSLGvrTopKDecodeRunner:
"""Runner for the GVR Top-K cuTe DSL kernel (Blackwell SM100).
Expand All @@ -7292,77 +7296,37 @@ def _pick_tuning(
max_seq_len: Optional[int],
data_ptr: int,
) -> dict:
"""Pick T / V / min_blocks_per_mp tuning knobs shared by
single-CTA / sort and LB compile paths. Returned keys match
``_compile`` / ``_compile_lb`` param names for ``**tuning``
spreading.
"""Adapter over :meth:`GvrTopKKernel.pick_tuning` (the single
source of truth for the T / V / min_blocks_per_mp /
warp-reduce policy), shared by the single-CTA / sort and LB
compile paths. Returned keys match ``_compile`` /
``_compile_lb`` param names for ``**tuning`` spreading.

Intentional shell divergence from ``GvrTopKKernel.launch``:
a 32B-misaligned logits pointer is a CONTRACT VIOLATION here
(assert), while ``launch`` silently downgrades to 128-bit
loads (dev convenience for ad-hoc tensors).
"""
enable_unroll_4 = True
enable_phase3_unroll = True
use_constant_hint = False

# T=1024 needs 1 CTA/SM grid AND enough per-CTA vec work.
# Under graph capture, raise the half-prec bar so a small
# capture-N doesn't force T=1024 on small-N replays
# (~14-16% regression).
if max_seq_len is not None and torch_dtype != torch.float32:
n_thresh_t = 131072
else:
n_thresh_t = 65536
num_threads_per_block = (1024 if
(num_rows <= num_sms
and N_per_cta >= n_thresh_t) else 512)
# V=256-bit only helps fp32 at large N. Half-prec cvt
# doubles reg pressure (5-11% loss at K=512/1024). Caller
# must hand a contiguous (32B-aligned) tensor — torch.empty
# / row slices satisfy this; column / stride-padded layouts
# may not.
use_256bit_load = (torch_dtype == torch.float32
and N_per_cta >= 16384)
if use_256bit_load:
cfg = _GvrTopKKernel.pick_tuning(
torch_dtype,
num_rows,
N_per_cta,
num_sms,
graph_capture=max_seq_len is not None,
)
if cfg["use_256bit_load"]:
assert data_ptr % 32 == 0, (
f"use_256bit_load=True requires 32B-aligned "
f"logits.data_ptr(), got {data_ptr} % 32 = "
f"{data_ptr % 32}.")
# Warp-parallel reduce only pays at 32-warp (T=1024).
enable_warp_parallel_reduce = num_threads_per_block == 1024

# min_blocks_per_mp: reg-vs-occupancy 3-tier. Half-prec
# prefers extra CTA/SM (cvt-ILP fits in 40 regs); fp32
# wants mb=2 (4-LDG ILP needs ~70 regs).
vec_bits_host = 256 if use_256bit_load else 128
vec_w_host = vec_bits_host // (32 if torch_dtype == torch.float32
else 16)
n_vec_iters = max(1,
N_per_cta // (num_threads_per_block * vec_w_host))
if torch_dtype == torch.float32:
if n_vec_iters < 4:
min_blocks_per_mp = 0
elif num_rows <= num_sms:
min_blocks_per_mp = 1
elif (num_sms * 2 < num_rows <= num_sms * 3
and N_per_cta <= 32768):
# mb=3 packs all CTAs in 1 wave; at N>=64K kernel
# is bandwidth-bound and mb=2 wins instead.
min_blocks_per_mp = 3
else:
min_blocks_per_mp = 2
else:
if num_rows > num_sms:
min_blocks_per_mp = 3
elif n_vec_iters < 4:
min_blocks_per_mp = 0
else:
min_blocks_per_mp = 1

return dict(
enable_unroll_4=enable_unroll_4,
enable_phase3_unroll=enable_phase3_unroll,
use_constant_hint=use_constant_hint,
num_threads_per_block=num_threads_per_block,
use_256bit_load=use_256bit_load,
enable_warp_parallel_reduce=enable_warp_parallel_reduce,
min_blocks_per_mp=min_blocks_per_mp,
enable_unroll_4=True,
enable_phase3_unroll=True,
use_constant_hint=False,
num_threads_per_block=cfg["num_threads"],
use_256bit_load=cfg["use_256bit_load"],
enable_warp_parallel_reduce=cfg["enable_warp_parallel_reduce"],
min_blocks_per_mp=cfg["min_blocks_per_mp"],
)

@classmethod
Expand Down Expand Up @@ -7569,6 +7533,23 @@ def forward(

``counters`` without ``order_row`` is rejected.
"""
# Tiered-GVR fast path: fp32 / next_n >= 1 (MTP) /
# cr in {1, 4} / npad <= 262144 decode rows route to the
# direct/reg/tp CuTe DSL tiers; everything else (half-prec, LB,
# oversize npad, hw cluster cap) falls through to the in-tree
# kernel below. ``order_row`` (the LJF hint dsa.py computes for
Comment thread
longcheng-nv marked this conversation as resolved.
# num_rows >= 2 * num_sms) is accepted and ignored: the GVR
# tiers launch per-row CTAs and do not consume the permutation.
# Host-only guard — no device sync. The op signature and output
# contract are unchanged (unordered int32 indices, -1 pad only
# for degenerate rows).
if _is_tiered_topk_supported(logits, pre_idx, seq_lens,
Comment thread
longcheng-nv marked this conversation as resolved.
output_indices, top_k, next_n,
compress_ratio, order_row, counters):
_tiered_topk(logits, pre_idx, seq_lens, output_indices, top_k,
next_n, compress_ratio)
return

cute_dtype = _TORCH_TO_CUTLASS_DTYPE[logits.dtype]
num_rows = logits.shape[0]
# seq_lens is request-level, logits is row-level (next_n
Expand Down Expand Up @@ -7621,21 +7602,8 @@ def forward(
f"prepare, or use the single-CTA path.")
else:
if cluster_size is None:
# B200 SXM5 synth-data tuning, 2026-06-10:
# N < 64K -> 1 (sync unrecouped)
# N >= 128K, BS <= 4 -> 8 (tiny grid)
# BS * cs <= num_sms -> cs (single-wave)
# else -> 1 (multi-wave loses)
if N_row < 65536:
cluster_size = 1
elif num_rows <= 4 and N_row >= 131072:
cluster_size = 8
elif num_rows * 4 <= num_sms:
cluster_size = 4
elif num_rows * 2 <= num_sms:
cluster_size = 2
else:
cluster_size = 1
cluster_size = _GvrTopKKernel.pick_cluster_size(
num_rows, N_row, num_sms)
if cluster_size > 1:
hw_max_cluster = _query_max_cluster_size()
if cluster_size > hw_max_cluster:
Expand Down Expand Up @@ -7697,7 +7665,7 @@ def forward(
# ``num_rows >= 2 * num_sms``. Physical meaning: wave-2 must fit a
# full SM-row's worth of CTAs so the sort has long-vs-short rows to
# swap. Below that threshold the win is noise / can regress a few
# percent (B200 N∈{8K,16K,32K} sweep 2026-06-23).
# percent (measured, N in {8K,16K,32K}).
@torch.library.custom_op("trtllm::cute_dsl_gvr_topk_decode",
mutates_args=("output_indices", ),
device_types="cuda")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
from .filtered_top_k_decode_varlen import FilteredTopKKernelVarlenDecode
from .filtered_top_k_varlen_util import FilteredTopKKernelVarlen
from .gvr_topk_decode import GvrParams, GvrTopKKernel
from .gvr_topk_decode_direct import DirectTopKKernel
from .gvr_topk_decode_dispatch import is_tiered_topk_supported, tiered_topk
from .gvr_topk_decode_reg import GvrRegKernel
from .gvr_topk_decode_tp import GvrTpKernel
from .single_pass_multi_cta_radix_topk import SinglePassMultiCTARadixTopKKernel

__all__ = [
Expand All @@ -25,4 +29,9 @@
"FilteredTopKKernelVarlenDecode",
"GvrParams",
"GvrTopKKernel",
"GvrTpKernel",
"GvrRegKernel",
"DirectTopKKernel",
"tiered_topk",
"is_tiered_topk_supported",
]
Loading
Loading