[ROCm][MoE] Add roofline profiling scope to the Triton W4A16 MoE GEMM#1019
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds apply()-level profiling scopes around the Triton W4A16 MoE GEMMs in HybridW4A16MoEExperts.apply() so profiler traces include MoE GEMM problem parameters (including valid_blocks) needed for downstream roofline attribution.
Changes:
- Introduces
_moe_gemm_triton_scope(...)to build arecord_functionname that includes GEMM dims andvalid_blocks(derived fromnum_tokens_post_padded). - Wraps both Triton prefill GEMM invocations (GEMM1 and GEMM2) with the new scope, while remaining a no-op unless profiling scopes are enabled.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
035c321 to
d19e0e0
Compare
Author
|
@mgehre-amd ping |
Wrap both Triton prefill GEMMs (fused_moe_kernel_gptq_awq) in apply() with an apply()-level record_function scope carrying the dims the roofline tool needs: M, N, K, E, top_k, the quant group size g, block_m, and valid_blocks (= num_tokens_post_padded // block_m, so valid_blocks*block_m is the padded row count the kernel actually computes). The raw kernel name exposes none of these. g is included because the per-group scale bytes and dequant FLOPs scale with K/g; this MoE path supports configurable group sizes (block_shape=[0, group_size]), so emitting the real g keeps roofline attribution correct for non-128 group sizes instead of silently assuming 128. The scope is emitted at the apply() call site rather than inside invoke_fused_moe_kernel_hybrid_triton because an apply()-level scope survives torch.compile replay and stays attached to the kernel. valid_blocks needs a device->host sync (num_tokens_post_padded.item()), so the scope is gated on VLLM_CUSTOM_SCOPES_FOR_PROFILING / VLLM_NVTX_SCOPES_FOR_PROFILING and is a nullcontext otherwise -- production pays nothing. AI assistance (Claude) was used. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: Robert Esclapez Garcia <robert.garcia@amd.com>
d19e0e0 to
4e5bff8
Compare
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.
Summary
This adds an
apply()-levelrecord_functionprofiling scope around the Triton W4A16 MoE prefill GEMMs (fused_moe_kernel_gptq_awq, viainvoke_fused_moe_kernel_hybrid_triton) inHybridW4A16MoEExperts.apply(), so a profiler trace carries the dimensions the roofline tooling needs to compute real FLOPs/bytes for these kernels. It is pure profiling instrumentation: it changes no kernel behavior or numerics, and it is a no-op (anullcontext) unless profiling scopes are explicitly enabled, so production pays nothing.Motivation
The raw kernel name in the profiler (
fused_moe_kernel_gptq_awq) exposes none of the GEMM dimensions, so the roofline tool cannot attribute FLOPs/bytes to the MoE GEMMs and they fall out of the roofline summary. Several quantities that matter are not derivable from any tensor shape in the trace: the per-call problem size (M, N, K, E, top_k); the quant group sizeg, since the per-group scale bytes and dequant FLOPs scale withK/gand this MoE path supports configurable group sizes (block_shape=[0, group_size]in compressed-tensors), so a non-128gwould otherwise be mis-attributed; andvalid_blocks— the number of non-padding alignment blocks the kernel actually runs (num_tokens_post_padded // block_m), so thatvalid_blocks * block_mis the padded row count the kernel really computes (the routing-dependent padding is what drives the gap between useful and computed FLOPs).What this adds
vllm/model_executor/layers/fused_moe/hybrid_w4a16_moe.py: a_moe_gemm_triton_scope(M, N, K, E, top_k, group_size, block_m, num_tokens_post_padded)helper that emitsmoe_gemm_triton {M}x{N}x{K} E={E} top_k={top_k} g={group_size} block_m={block_m} valid_blocks={valid_blocks}, and wraps both Triton prefill GEMMs (gemm1 up/gate proj and gemm2 down proj) inapply()with it. Thegfield is the realself._group_size, not a hardcoded 128.Design notes
The scope is emitted at the
apply()call site rather than deep insideinvoke_fused_moe_kernel_hybrid_tritonon purpose: anapply()-level scope survivestorch.compilereplay and stays attached to the compiled kernel, whereas a scope inside the launch helper is orphaned from the compiled kernel and leaves the MoE GEMM with no roofline.valid_blocksrequires readingnum_tokens_post_padded.item()(a device→host sync), so the scope is gated onVLLM_CUSTOM_SCOPES_FOR_PROFILING/VLLM_NVTX_SCOPES_FOR_PROFILINGand returns anullcontextwhen neither is set — the sync (and the scope) only happen during profiling runs.vllm bench serve --profilesetsVLLM_CUSTOM_SCOPES_FOR_PROFILING=1automatically.