Repository navigation
[Kernel] Scale elementwise kernel launches with the device, not just batch size - #61404
Open
XiaobingSuper wants to merge 2 commits into
Open
XiaobingSuper wants to merge 2 commits into
XiaobingSuper wants to merge 2 commits into
Conversation
…batch size silu_and_mul and the other act_and_mul variants, the scalar activations and rotary_embedding all launch grid(num_tokens) with one block per token, striding that block over the hidden dim. The grid is therefore as wide as the batch however much work each token carries. At decode num_tokens is the batch size, so a batch of 8 launches 8 blocks and leaves the rest of the device idle; the kernels are nowhere near memory bound at that point, they simply have no blocks to run. Add vllm_elementwise_launch_config() in launch_bounds_utils.h, shared by both files. Once num_tokens alone covers the SM count it returns the existing configuration (chunks == 1), so large-batch launches are unchanged; below that it uses smaller blocks and splits a token's work over chunks blocks in gridDim.y, making the launch scale with the device. Every element is still handled by exactly one thread reading the same inputs, so results are unchanged bit for bit. Measured on MI355X (gfx950, 256 CUs), ROCm 7.2.4, timed by HIP-graph replay, bf16 and fp16, 288 shapes: silu_and_mul d=28672, 1-16 tokens 7.2-8.0us -> 2.2-2.5us up to 3.42x silu_and_mul d=14336, 64 tokens 4.52us -> 2.82us 1.60x rotary_embed 32 heads, 8-32 tokens 4.3-4.4us -> 2.3us up to 1.91x median over all shapes: silu 1.31x, rope 1.19x >= 256 tokens (chunks == 1): 0.994-1.004x, i.e. unchanged All 288 shapes produce bit-identical output against the unpatched build. Signed-off-by: XiaobingZhang <xiaobingzhangupc@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
XiaobingSuper
force-pushed
the
elementwise-launch-config
branch
from
October 13, 2026 13:16
9d1f10b to
a6b4c82
Compare
…evice Both kernels launch grid(num_tokens) with one block per token and stride that block over the hidden dim, so the grid is only as wide as the batch however much work a token carries. At decode that leaves most of the device idle: one token is one block, whatever the hidden size. They have the same shape as the act_and_mul family, so they can reuse vllm_elementwise_launch_config(): when num_tokens alone covers the SM count it returns the previous configuration and the launch is unchanged, and below that it splits a token's work over gridDim.y. Each element is still handled by exactly one thread reading the same inputs, so results do not move. Measured on MI355X (gfx950, 256 CUs), ROCm 7.2.4, HIP-graph replay, bf16 and fp16, hidden 4096 / 14336 / 28672, 1 to 2048 tokens: situ_and_mul d=28672, 1-2 tokens 14.9-15.3us -> 2.1-2.2us 7.0x situ_and_mul d=28672, 8 tokens 15.5us -> 2.5us 6.2x swigluoai_and_mul d=28672, 1 token 9.0us -> 2.1us 4.4x Median over all 108 shapes is 1.58x. Where the batch already fills the device the configuration is unchanged by construction, and the measurement agrees: the slowest case is 0.98x. All 108 shapes produce bit-identical output against the unpatched build, compared by SHA-256 of the raw bytes. Signed-off-by: XiaobingZhang <xiaobingzhangupc@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6 tasks done
Contributor
Author
|
@tjtanaa please help review it. Thanks, |
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
silu_and_muland the otheract_and_mulvariants, the scalar activations,swigluoai_and_mul,situ_and_mulandrotary_embeddingall launchgrid(num_tokens)with one block per token, so the grid is only as wide as the batch however much work a
token carries. At decode that leaves most of the device idle. This splits a token's work
across
gridDim.ywhen the batch alone cannot fill the device.Claims
situ_and_mul, 4.36x onswigluoai_and_mul, 3.42x onsilu_and_muland 1.91x on
rotary_embeddingat decode-sized batches.configuration, and measurements there are 0.994x to 1.004x.
Validation
Bitwise: 288 shapes (
silu_and_mulandrotary_embedding, bf16 and fp16, 1 to 2048tokens, neox and non-neox, several head counts) compared with
torch.equalagainst theunpatched build — not a tolerance.
Performance, MI355X (gfx950, 256 CUs), ROCm 7.2.4, HIP-graph replay:
situ_and_mulswigluoai_and_mulsilu_and_mulsilu_and_mulrotary_embeddingMedians: 1.31x over 288 activation/rope shapes, 1.58x over 108 gated-activation shapes.
Where the batch already fills the device the configuration is unchanged by construction
and the measurements agree — 0.994x to 1.004x for the first group, 0.98x worst case for
the second.
situ_and_mulgains the most because its loop is scalar, so it had no other source ofparallelism within a token.
This change is not ROCm-specific and has also been verified on NVIDIA hardware, where it
likewise shows a gain.
Details
Root cause. These kernels launch one block per token and stride that block over the
hidden dim, so a batch of 8 launches 8 blocks. They are nowhere near memory bound at that
point — they simply have no blocks to run.
Change.
vllm_elementwise_launch_config()incsrc/launch_bounds_utils.h, shared byevery call site in
activation_kernels.cuandpos_encoding_kernels.cu. Whennum_tokens >= sm_countitreturns the existing configuration (
chunks == 1) so large-batch launches are untouched;below that it uses smaller blocks and splits a token's work over
chunksblocks ingridDim.y. Every element is still handled by exactly one thread reading the same inputs,which is why results do not move.
Where this helper fits. It suits kernels whose loop strides over the hidden dim, so
any
(threads, chunks)split is valid. It does not suit kernels that give eachblockIdx.ya contiguous chunk and vectorize inside it — there a small chunk leaves toofew elements per thread to form a wide load. All five kernels here are the first kind.
Scope. The activation and rope ops are
CustomOps, andCustomOp.default_on()is false whenInductor is in use, so in the default compiled configuration they are replaced by
Inductor-generated Triton kernels and this patch does not run. A Llama-3.1-8B decode
profile confirms it: the activation and norm work appears as
triton_poi_fused_*with novllm::act_and_mul_kernelat all. Where the patch does apply —enforce_eager, or-O.custom_ops=+silu_and_mul/+rotary_embedding— the same profile shows these kernelson the decode path at 4.6us and 4.4us per call, which is the regime measured above. So
this is a kernel-level fix for the configurations that use these kernels, not an
end-to-end win for the default Llama path.
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: One helper in an existing header, reused by both call sites; no API
or caller change, and the previous configuration is what it returns at large batch.
Testing and Validation: Bitwise sweep above; performance numbers need an MI355X
and are not reproducible in CI.
Code Quality and Style:
pre-commitclean; comments say why, not what.Pull Request Contents: Claims, evidence, root cause and scope limits above.