[None][perf] DSv4 indexer Top-K: align kFTarget=kK, uniform kSeqSmall=4096, diagnostic env knobs#14413
Conversation
|
/bot run --disable-fail-fast |
|
PR_Github #49702 [ run ] triggered by Bot. Commit: |
|
PR_Github #49702 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
|
/bot run --disable-fail-fast |
|
PR_Github #49855 [ run ] triggered by Bot. Commit: |
|
PR_Github #49855 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
|
PR_Github #50037 [ run ] triggered by Bot. Commit: |
|
PR_Github #50037 [ run ] completed with state |
f779519 to
cee7a41
Compare
…=4096, diagnostic env knobs
Three correlated tuning changes for the V4 indexer Top-K Heuristic
dispatch on Blackwell GPUs. All three exercise the same code path
(invokeIndexerTopKDecode / Dtype) and share the same B200/B300
swe-bench synth benchmark anchor, so they ship together.
1) heuristic_topk.cuh: align kFTarget to kK for K=512/1024 (all dtypes)
GvrParams<{fp32,bf16,fp16}, 512>: kFTarget = 384 -> 512 (= kK)
GvrParams<{fp32,bf16,fp16}, 1024>: kFTarget = 2560 -> 1024 (= kK)
GvrParams<*, 2048>: unchanged (kFTarget = 3072 fp32 / 4096 bf16,fp16).
The secant solver's soft-target is now aligned with the band's lower
edge. Eliminates the upper-clamp saturation observed on tight-sigma
high-A2 layers (K=512) and corrects a mistune for K=1024 that was
measured against M=512 instead of production M=K=1024.
Measured (B200, swe-bench synth, mean over 3 cfgs, zero cap-hits,
zero per-layer regression):
K=512 P2-iter sum: 2.19x / 1.77x / 1.51x at N=32K/64K/100K
K=1024 P2-iter sum: 1.17x at N=32K (M=K=1024)
2) indexerTopK.cu: uniform kSeqSmall = 4096 across all K
kSeqSmallDefaultForK now returns 4096 for every topK (previously
4096 for K=512/1024 and 12288 for K=2048). This aligns the GVR
routing boundary with the Radix multi-CTA split-work threshold
(maxByCols = N / kDecodeMinColsPerSubBlock(=2048) >= 2 at N >= 4096),
so the dispatcher's algorithmic-handoff point is consistent across K:
below 4096 the Radix path resolves to single-CTA insertion-sort and
GVR is not attempted; at or above 4096 GVR may be considered.
Measured (B300, V3.2-Q19c protocol, May 2026):
N=4K cells across K ∈ {512, 1024, 2048} all win — GVR R/H bf16 =
3.07x (K=512) / 2.57x (K=1024) / 1.34x (K=2048).
N=2K cells across the same 9 (K x dtype) combos all show GVR
R/H < 1 (0.55x – 0.84x), justifying 4K as the floor.
All four call sites (computeIndexerTopKDecodeBlocksPerRow, the fp32
dispatcher, the bf16/fp16 dispatcher, canIndexerTopKDecodeUseGvr)
retain their topK arg even though the kSeqSmall lookup itself is now
K-agnostic; kept for API stability and because kBsWave/kBsL2/kBsLarge
are still K-aware via bytesPerElem.
3) indexerTopK.cu: diagnostic / tuning env knobs (off by default)
TRTLLM_HEURISTIC_NMIN (existing): now becomes an escape-hatch that
wins over the uniform default when set; previously it just shadowed
a single global default.
TRTLLM_HEURISTIC_BSMAX (new): overrides kBsLarge past the
hardware-derived min(kBsWave, kBsL2). Higher risk than NMIN — it
bypasses L2/occupancy safety bounds, so set only for indexer
microbenchmarks needing a BS-scaling comparison against the Radix
path on identical inputs. The hardware-derived defaults are
expected to be optimal for production; both env knobs are
documented in-source as diagnostic/tuning escape hatches.
Tests (test_indexer_topk_decode_dist + _dist_v4_cr4):
- V3.2 cr=1: num_tokens=[8192,16384] -> [4096,8192,16384] to cover the
new uniform K-agnostic kSeqSmall=4096 boundary.
- V4 cr=4: num_tokens=[65536,131072] -> [32768,65536,131072]; helper's
hardcoded min_uncompressed=12288 floor replaced with the uniform
ksmall=4096 floor + pytest.skip guard for combos where the clamp
floor exceeds num_tokens. With ksmall=4096 the floor at cr=4 is
~16389 uncompressed, so num_tokens=32768 admits all K ∈ {512, 1024,
2048} without skipping.
Made-with-Claude-Code: opus-4.7-1m
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com>
cee7a41 to
a2d0a71
Compare
|
/bot run --disable-fail-fast |
|
PR_Github #50102 [ run ] triggered by Bot. Commit: |
|
PR_Github #50102 [ run ] completed with state |
|
@lfr-0531 @mingyangHao Could you please take a look when you have a chance? Current status of this PR:
The change is the DSv4 indexer Top-K tuning (align |
|
E2E MMLU accuracy validation — completed Ran MMLU 5-shot (chat-mode, full 14 042 samples) on the post-PR HEAD Setup
Result — Flash MMLU 5-shot, 14 042 samples
Comparison vs reference baselines (same TP/EP/MTP/CUDA-Graph/autotuner)
The Δ of +0.50 pp vs baseline is ~1.4× the 1σ binomial stderr for N=14 042 (≈0.35 pp) — well within MMLU 5-shot chat-mode run-to-run noise (see prior 2×2 isolation in our internal validation report, where individual MMLU 5-shot runs at fixed config can drift several pp due to 1-token-output reduction-order sensitivity). No regression from the indexer Top-K dispatch tuning. LogLog (this host): @lfr-0531 @mingyangHao — ready for review/approve. |
Mirrors the kFTarget=kK alignment for K=512/1024 (all dtypes) from CUDA PR NVIDIA#14413 on the DSL GVR Top-K kernel so the DSL Phase-2 secant behavior matches the new CUDA reference. Old pre-NVIDIA#14413 values kept as inline comments for easy rollback. Verified: 768/768 pytest configs pass for K=512/1024 across all dtypes, N, next_n, use_256bit_load, num_threads_per_block, and enable_warp_parallel_reduce. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
Mirrors heuristicTopKDecode.cu PR NVIDIA#14219 cr-aware branch in the DSL GVR Top-K kernel. compress_ratio=1 (default) preserves DSv3.2 behavior exactly; compress_ratio=4 enables the DSv4 (overlap-compressor) indexer path: * pre_idx_offset = 0 (vs (row % next_n) + 1 for cr=1) — in compressed- index space, new entries append at the end so prev-step indices remain valid as-is. * N = actual_kv_len / cr — logits/preIdx live in compressed-token- index space when cr > 1. GvrParams TABLE is also keyed by (dtype, K, cr) so V3.2 and V4 use their respectively tuned kFTarget values: cr=1 (V3.2): kFTarget = 384 (K=512) / 2560 (K=1024), pre-NVIDIA#14413. cr=4 (V4): kFTarget = kK = 512 (K=512) / 1024 (K=1024), PR NVIDIA#14413. K=2048: identical across cr (V4 doesn't natively use K=2048). Cache key includes compress_ratio so different cr settings compile separate kernels. assert restricts compress_ratio in {1, 4}. Verified: 1152/1152 pytest configs pass on cr=1 default path. Signed-off-by: Mindy Li <11663212+limin2021@users.noreply.github.com>
…=4096, diagnostic env knobs (NVIDIA#14413) Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
…=4096, diagnostic env knobs (NVIDIA#14413) Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
…=4096, diagnostic env knobs (NVIDIA#14413) Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
…=4096, diagnostic env knobs (NVIDIA#14413) Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com>
…=4096, diagnostic env knobs (NVIDIA#14413) Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com>
…=4096, diagnostic env knobs (NVIDIA#14413) Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com>
…=4096, diagnostic env knobs (NVIDIA#14413) Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com>
…=4096, diagnostic env knobs (NVIDIA#14413) Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com>
…=4096, diagnostic env knobs (NVIDIA#14413) Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com>
…=4096, diagnostic env knobs (NVIDIA#14413) Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: longcheng-nv <243710427+longcheng-nv@users.noreply.github.com>
Summary
Three correlated tuning changes for the V4 indexer Top-K Heuristic dispatch on
Blackwell GPUs, all exercising the same
invokeIndexerTopKDecode/invokeIndexerTopKDecodeDtypecode path and sharing a single B200/B300swe-bench-synth benchmark anchor (V3.2-Q19c protocol). Ships as one PR.
Code changes
1.
kFTarget = kKalignment for K=512/1024 (heuristic_topk.cuh)kFTargetkFTarget<{fp32,bf16,fp16}, 512><{fp32,bf16,fp16}, 1024><*, 2048>Aligns the secant solver's soft-target with the band's lower edge. Eliminates
upper-clamp saturation on tight-σ high-A2 layers (K=512) and corrects a
mistune for K=1024 originally calibrated against M=512 (Flash inheritance)
instead of production M=K=1024.
Full GVR
GvrParams<InputT, TopK>table (post-PR,heuristic_topk.cuh:224-313).f = kNumBins(P4 histogram bin count) ·C = kC(candidate buffer cap) ·f_target = kFTarget(P2-secant soft target for early termination):★
(fp32, K=2048)is V2e SASS byte-identity locked —kFTargetandkCcannot be moved without altering ptxas output for the V3.2 productionhot path; this PR preserves the legacy
(3072, 6144)tuple for thatsingle cell. All other 8 cells use
kC=5120(sized for 4–5 CTA/SMoccupancy headroom);
kFTarget=kKfor K=512/1024 across all dtypes.kNumBinsis per-(T, K) empirical sweet spot under the atomic-contentionvs P4-setup trade-off (
heuristic_topk.cuh:207-216).2. Uniform
kSeqSmall = 4096across all K (indexerTopK.cu)kSeqSmallDefaultForKnow returns 4096 for everytopK(previously4096 for K=512/1024, 12288 for K=2048). Aligns the GVR routing boundary
with the Radix multi-CTA split-work threshold
(
maxByCols = N/kDecodeMinColsPerSubBlock(=2048) ≥ 2 at N ≥ 4096): below4096 the Radix path resolves to single-CTA insertion-sort and GVR is not
attempted; at or above 4096 GVR may be considered. Consistent algorithmic
handoff across all K.
3. Diagnostic / tuning env knobs (
indexerTopK.cu)TRTLLM_HEURISTIC_NMINkSeqSmallfor all KTRTLLM_HEURISTIC_BSMAXkBsLargepast hardware-derivedmin(kBsWave, kBsL2)Both default off. Documented in-source as diagnostic escape hatches.
NMINsemantics refactored to become a true escape-hatch that overrides the
default when set (previously shadowed a single global default); backward
compatible.
Validation
Correctness (B200 + B300)
test_indexer_topk_decode_radix_aux_cuda_graph_replaytest_indexer_topk_decode_launch_policy_transitions(BS 1/64/100/132/148/256)test_indexer_topk_decode_dist -k 4096(V3.2 cr=1)test_indexer_topk_decode_dist_v4_cr4full matrix (162 cells)TRTLLM_HEURISTIC_NMIN=8192 pytest -k "1024 and 4096"env-override smokeTRTLLM_HEURISTIC_BSMAX=256 pytest -k "1024 and 4096"env-override smoketests/unittest/api_stability/K-aware clamp guard auto-skipping (now uniform 4K floor — see §"Test plan")
is the intended new behaviour, observed by
pytest.skiprather than failure.BSMAXdispatch-shift evidence (TRTLLM_SCHEMEX_DEBUG=1, K=1024 fp32, N=16256)numRowskBsLarge=426, hw-default)BSMAX=256(kBsLarge=256)BSMAXcorrectly bypasses the hardware-derived L2/occupancy bound.(Note:
[Scheme X]debug trace only emits from the fp32 dispatcher;driving with
DTYPE=torch.float32+pytest -sis required to observe it.)Accuracy — GSM8K (8×B300, 1319 problems, GVR ON unless noted)
GVR-OFF control: Δ = +0.04, well within ±0.51 stderr → dispatch tuning is
accuracy-neutral. Both Flash/Pro variants well within ±stderr of the prior
B300 reference (Pro 96.66 / 96.59 from
feat/deepseek_v4HEAD before this PR).Performance — B200 swe-bench-synth anchor (V3.2-Q19c protocol)
P2-iter reductions from §1 (zero cap-hits, zero per-layer regression,
3-cfg mean):
GVR R/H over Radix-fp32 baseline (bf16, BS=1) on B200:
Performance — B300 V3.2-Q19c protocol (this PR's HEAD)
Important: the Radix baseline in this PR has already been upgraded to a
multi-CTA split+merge dispatcher (introduced #13811 [
computeIndexerTopKDecodeBlocksPerRow,kMaxBlocksPerRowDecode=10,kDecodeMinColsPerSubBlock=2048] and madedevice-aware in #13886 [SM-count + wave-quantization sweep]). For BS=1 cells
in this sweep, the Radix path uses
bpramping from 2 (N=4K) to 10 (N≥32K),not single-CTA radix sort. This is the strongest possible Radix baseline
— the GVR R/H ratios below are vs "Radix at its current best", not vs
legacy single-CTA. The flat Radix wall in 4K–32K (37.8→40.9 µs, +8%) is a
direct consequence of bp ramp masking per-CTA work; the kink at N≥32K (bp
caps at 10) is when Radix begins linear HBM scaling.
GVR R/H over Radix-fp32 baseline (bf16), kernel-isolated, cold-L2 (128 MiB
flush, 256 MiB rerun confirms ≤ 0.10× delta — sizing rule
flush ≥ max(BS×N×4B)suffices). Source-of-truth:_bench_n_bs_v32proto.py.N-axis at BS=1:
BS-axis at N=32K:
† N=2048 boundary cell: Radix path falls through to insertion-sort
single-CTA (
maxByCols = N/2048 = 1→bp=1;useRadixSort = N≥12288 = false). All 9 (3K × 3dtype) cells at N=2K show R/H ∈ [0.55, 0.84], all< 1 — confirms
indexerTopK.cu:736-742source comment ("K=512 R/H = 0.73×at N=2K") and extends it to K=1024/2048 + bf16/fp16. This is the
justification for the uniform
kSeqSmall=4096floor: below 4K all Klose, at/above 4K all K win.
Summary: Across 198 Flash cells, 198 Pro cells, 198 K=2048 V3.2-anchor
cells (594 total) at N ≥ 4K: GVR wins every cell except one (K=2048 fp32
N=131K = 0.99× parity; not in DSv4 production K set). Peak 5.83× at
Flash BS=1024 N=32K bf16.
B300 single-op B@cuda_graph=null E2E (paired before/after on identical workload)
trtllm-benchFlash, ISL=1024 / OSL=1024 / 96 seqs / MTP=3 / GVR ON / NVFP4 + FP8 KV.Base lib built from
HEAD~1(bc6aa89af6), PR-head lib built from this PR.Same node, libs swapped between runs (md5 distinct;
BSMAXsymbol only in PR-head).+3.5% E2E throughput on B300 Flash at cuda_graph=null from the
kFTarget=kK + uniform-kSeqSmall tuning, no regression on other metrics.
Test plan
Extended
tests/unittest/_torch/thop/parallel/test_indexer_topk.py:test_indexer_topk_decode_dist):num_tokens = [8192, 16384]→[4096, 8192, 16384]. Adds +1134 cellscovering the new uniform
kSeqSmall=4096boundary across all K.test_indexer_topk_decode_dist_v4_cr4):num_tokens = [65536, 131072]→[32768, 65536, 131072].Helper's hardcoded
min_uncompressed=12288floor replaced with uniformksmall=4096+pytest.skipguard for combos where the clamp floorexceeds
num_tokens. With uniform 4K the floor at cr=4 is ~16389uncompressed, so
num_tokens=32768admits all K∈{512,1024,2048} withoutskipping (better coverage than the K-aware draft).
Notes for reviewers
heuristic_topk.cuh.Production DSv4 does not use K=2048. Extending
kFTarget=kKto K=2048is a potential follow-up; out of scope.
kSeqSmall=4096rationale: the N=2K cells confirm 4K is thecorrect universal floor across all K (insertion-sort wins below, GVR
wins from 4K onward including K=2048 at N=4K = 1.34× bf16 / 1.32× fp16
per B300 V3.2-Q19c sweep).
TRTLLM_HEURISTIC_BSMAXprimary user is the cross-family indexermicrobench harness; production paths should not need to set it.
not in committed source. Full benchmark methodology + raw R/H tables in
internal sweep report; available on request.
Branch / build sanity
a2d0a71b34(rebased ontoorigin/feat/deepseek_v4at818621e1c9= post-[None][fix] DSv4 indexer: resize radix aux scratch in update_spec_dec_param #14443 merge). Earlier validation cycles ran onf779519e3d(pre-rebase); kernel-level content identical between the two,only
kSeqSmallsimplified from K-aware to uniform 4K.make -C cpp/build -j th_common; PTX JIT from sm_100 cubinloads cleanly on B300 (sm_103).
libth_common.somd5 confirmed on every eval run viagrep libth_common.so /proc/<eval-pid>/maps+md5sum.🤖 Generated with Claude Code