Repository navigation
Conversation
…shapes The shape-dynamic skinny GEMM (vllm-project#62956) serves unquantized BF16 linear layers and LM heads at 1-4 tokens when a measured plan exists for the device, but SM90 had no plans, so H100/H200 decode stayed on cuBLAS. Add plans for the TP=1 local shapes of Qwen3-4B/8B/14B/32B and Llama-3.1-8B, measured on an H200: 52 cells that beat cuBLAS by >= 5% with a cold L2 and >= 3% with a hot L2. Every 4-token cell was also checked inside a running model (nsys at 4 concurrent requests); Llama-3.1-8B down and Qwen3-14B QKV at 4 tokens win in isolation but lose there and are left out. static_k entries carry no k_unroll (the static path is fully unrolled). Tests: the plan run-through test is parametrized over every capability table and calls the kernel directly; the layer selection test covers the SM90 table as well. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Zhifeng Li <56554081+Rockyeast@users.noreply.github.com>
Rockyeast
requested review from
AndreasKaratzas,
WoosukKwon,
yewentao256 and
zyongye
as code owners
November 1, 2026 11:58
This was referenced Nov 1, 2026
Open
H200, same method as the other cells: o_proj (2048, 4096) 1.70x / 1.80x / 1.54x at 1 / 2 / 4 tokens, fused QKV (5120, 2048) 1.32x / 1.17x, LM head (151936, 2048) 1.09x / 1.09x; the 1.03x and 1.01x cells at 4 tokens stay on cuBLAS. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Zhifeng Li <56554081+Rockyeast@users.noreply.github.com>
Rockyeast
marked this pull request as draft
November 3, 2026 04:03
6 tasks done
This branch has not been deployed
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.
Overview
#62956 routes unquantized BF16 linear layers and LM heads through the shape-dynamic CuTe DSL skinny GEMM when
skinny_gemm_plans.pyhas a measured plan for the device, the local(N, K)and the token count. SM90 has no plans, so on H100 / H200 every dense decode GEMM still goes to cuBLAS, which reads the weight at 2.3-3.7 TB/s at 1-4 tokens.This PR adds
SM90_SKINNY_GEMM_PLANSfor the TP=1 local shapes of Qwen3-4B / 8B / 14B / 32B and Llama-3.1-8B (QKV, o_proj, fused gate/up, down, LM head), registered under capability(9, 0). No code path changes: shapes or token counts without a plan keep using cuBLAS.Claims
Qwen/Qwen3-30B-A3B(BF16 MoE,torch.compile), added after the first review round: three dense cells (fused QKV(5120, 2048), o_proj(2048, 4096), LM head(151936, 2048)): median TPOT -5.5% at one request (4.755 -> 4.493 ms), -2.6% at 2, about -3% at 4, unchanged at 8 (no plan).Validation
Per-cell GEMM time
H200 NVL, vLLM nightly
0.33.0rc1.dev421with #62956 applied, BF16, CUDA graph replay, the weight rotated over enough copies that every call reads it from HBM, and each GEMM preceded by a small non-PDL elementwise kernel (as in the model; its time is subtracted). Median of 7 paired repeats, cuBLAS -> plan in us:Kept: >= 5% faster with a cold L2 and >= 3% faster with a hot L2 (single weight copy), plus the in-model check below for 4 tokens. cuBLAS cells did not pass. At 8 and 16 tokens the skinny GEMM loses on every shape (0.4-0.9x), so no plans are listed there.
E2E
H200 NVL, TP=1,
vllm serve <model> --max-model-len 4096 --no-enable-prefix-caching,vllm bench serve --dataset-name random --random-input-len 1024 --random-output-len 256 --ignore-eos --seed 0; 32 prompts at concurrency 1, 8x the concurrency otherwise. Both arms run the same nightly image with #62956's files applied; the PR arm adds this plan file.VLLM_DISABLE_COMPILE_CACHE=1in both arms (see Details). Two runs per arm, interleaved on two GPUs with the GPUs swapped. Median TPOT (ms), both runs:Output throughput moves with TPOT (Qwen3-8B at concurrency 1: 162.7 -> 190.2 tok/s). TTFT is unchanged within run-to-run spread.
Qwen/Qwen3-30B-A3B(BF16), same setup, four launches on one GPU in the order base, PR, PR, base; the routed experts run the Triton fused MoE in both arms, the plans only cover the dense projections and the LM head:Per-cell (H200, cold L2, cuBLAS -> plan, us): o_proj 9.42 -> 5.53 (1.70x) / 10.31 -> 5.74 (1.80x) / 10.19 -> 6.64 (1.54x) at 1 / 2 / 4 tokens; fused QKV 8.81 -> 6.69 (1.32x) / 8.49 -> 7.23 (1.17x), 4 tokens left to cuBLAS (1.03x); LM head 155.4 -> 142.2 (1.09x) / 156.4 -> 143.2 (1.09x), 4 tokens left to cuBLAS (1.01x).
Tests
pytest tests/kernels/test_bf16_skinny_gemm.pyon H200 (nightly image with #62956 and this change applied): 369 passed, 438 skipped (other architectures).test_skinny_gemm_plans_are_valid(existing, parametrized over every capability) covers the new entries.test_skinny_gemm_plans_run_and_match_linear, parametrized over every capability table (so it also covers the SM121 entries on a GB10): callsshape_dynamic_skinny_gemmdirectly with the plan's config (a silentF.linearfallback cannot make it pass), asserts the kernel was compiled and compares withF.linear; N is capped at 4096 so the LM-head cases do not allocate 1.5 GB weights.test_maybe_use_skinny_gemmis now parametrized over the capability tables with one planned shape each.Details
(4096, 14336)(1.09x isolated, 1075 -> 1349 us in the model, made concurrency 4 3% slower) and Qwen3-14B QKV(7168, 5120)(1.15x isolated, 970 -> 1025 us in the model).torch.compiletraces the model, and the plan file is not part of the compile cache key. Two servers of the same vLLM build, one with and one without a plan, can therefore load each other's cached graph. All E2E numbers above useVLLM_DISABLE_COMPILE_CACHE=1; without it, a second run of the PR arm reproduced the baseline.nvjettiles (often split-K with a separate reduce kernel) that reach 2.3-3.7 TB/s on these weights; the skinny GEMM streams the weight once with vectorized loads, reaches 3.6-4.3 TB/s, and removes the reduce kernel.(9, 0)and were measured on an H200 only (132 SMs, 4.8 TB/s). The kernel is bandwidth bound at these sizes, so the ranking should hold on H100, but the margins were not measured there and the table header says so. I can re-measure on an H100 if a reviewer has one; otherwise the existing per-capability keying is what the SM121 table also relies on.Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.
🤖 Generated with Claude Code