Skip to content

bench: Unify PDL behavior, add missing norm routines, and misc improvements - #3435

Merged
bkryu merged 1 commit into
flashinfer-ai:mainfrom
bkryu:benchmark_improvements
May 28, 2026
Merged

bkryu merged 1 commit into
flashinfer-ai:mainfrom
bkryu:benchmark_improvements

Conversation

@bkryu

@bkryu bkryu commented May 28, 2026

Copy link
Copy Markdown
Collaborator

📌 Description

Reshapes flashinfer_benchmark.py to (a) make --enable_pdl behave uniformly across every routine, (b) add three previously-missing norm benchmarks, and (c) fix a few backend-label / API-routing bugs surfaced along the way.

--enable_pdl uniform semantics

A single rule across all 50+ routines: --enable_pdl omitted → PDL off; --enable_pdl set → PDL on; routines whose API can't accept PDL emit a one-shot [WARNING] and ignore the flag.

  • Hoisted --enable_pdl to the shared parser in flashinfer_benchmark.py; removed three duplicate declarations in parse_norm_args / parse_gemm_args / parse_quantization_args.
  • Added warn_if_pdl_unsupported(args, routine) helper in flashinfer_benchmark_utils.py.
  • Fixed silent-ON bugs (APIs default enable_pdl=None → auto-enable on H100/B200, but benchmark wasn't passing it): all 4 attention wrappers (decode / prefill paged / prefill ragged / MLA) + 4 direct
    trtllm/MLA call sites, mm_fp4 (API default was True!), softmax, plus 4 MoE routines (trtllm_fp4_block_scale_moe, trtllm_fp8_per_tensor_scale_moe, cutlass_fused_moe, cute_dsl_fp4_block_scale_moe).
  • Fixed hardcoded enable_pdl=True: moe.py (trtllm_fp8_block_scale_moe) and moe_comm.py (trtllm_fp8_block_scale_routed_moe in a2a dispatch).
  • Fixed hardcoded enable_pdl=False blocking opt-in: two trtllm_ragged_attention_deepseek call sites in attention.py (cute-dsl and trtllm-native branches).
  • Wired CLI control for off-by-default APIs: 3 rope quant variants (mla_rope_quantize_fp8, rope_quantize_fp8, rope_quantize_fp8_append_paged_kv_cache).
  • Added 30 warn_if_pdl_unsupported calls to routines whose APIs don't accept enable_pdl: 6 gemm, 5 rope, 14 sampling, 1 quantization (nvfp4_batched_quantize), 1 mamba, 1 moe (b12x_fused_moe),
    allreduce_fusion, mixed_comm.
  • Net: ~50 call-site changes; verified with on/off pairs that produce measurable perf deltas (e.g. attention prefill paged fa2: 71→49 μs with --enable_pdl).

New norm benchmarks

Three rmsnorm-family routines added to benchmarks/routines/norm.py, ported from a side branch and stripped of unrelated tooling:

  • fused_add_rmsnorm
  • gemma_rmsnorm
  • gemma_fused_add_rmsnorm

Registered in flashinfer_benchmark_utils.py (benchmark_apis, routine_cc_to_supported_backends), documented in benchmarks/README.md (bullets + support matrix), and exercised by new
samples/sample_testlist.txt entries.

Backend label corrections (cuda → cute-dsl)

Six rmsnorm-family routines (rmsnorm, rmsnorm_quant, fused_add_rmsnorm, fused_add_rmsnorm_quant, gemma_rmsnorm, gemma_fused_add_rmsnorm) actually route to CuTe-DSL kernels by default in flashinfer
(env-var fallback to CUDA JIT via FLASHINFER_USE_CUDA_NORM=1), but the benchmark was labeling them as cuda. Updated the cc-support map and run_backend dispatch to use cute-dsl, with a cuda → cute-dsl
auto-remap matching the existing FP4 routine pattern so sample lines don't need explicit --backends. README support matrix updated.

rmsnorm_quant CUDA-graph capture fix

testRmsnormQuant / testFusedAddRmsnormQuant were constructing scale as a Python float, which forced flashinfer's _normalize_scale_tensor to allocate a host tensor on every call — illegal under
CUDA-graph capture (and explicitly deprecated by a FutureWarning). Now pre-allocate scale as a shape-(1,) GPU float32 tensor once in the test function; CSV scale column unchanged thanks to .item()
on emit.

testBmmFp8 refactor (programmatic backend probing)

bmm_fp8 test has been checking hard-coded backend support, leading to gaps (e.g. cutlass is supported on SM120 but was not runnable) Aligned with testMmFp4: removed the hard-coded routine_cc_to_supported_backends["bmm_fp8"] table and the if backend in [...] whitelist inside run_backend. The test now probes each backend with a trial
flashinfer.gemm.bmm_fp8(...) call and relies on the @backend_requirement decorator to raise BackendSupportedError for unsupported (routine, backend, cc) combos. Future SM/backend additions no longer need
to touch the benchmark utils.

Misc

  • samples/sample_testlist.txt: added 9 new norm-variant sample lines (3 routines × 3 configs each); removed the rmsnorm_quant_fp8_e5m2 line (cute-dsl path is missing the dtype-string entry upstream —
    separate bug, not introduced here).

🔍 Related Issues

🚀 Pull Request Checklist

Thank you for contributing to FlashInfer! Before we review your pull request, please make sure the following items are complete.

✅ Pre-commit Checks

  • I have installed pre-commit by running pip install pre-commit (or used your preferred method).
  • I have installed the hooks with pre-commit install.
  • I have run the hooks manually with pre-commit run --all-files and fixed any reported issues.

If you are unsure about how to set up pre-commit, see the pre-commit documentation.

🧪 Tests

  • Tests have been added or updated as needed.
  • All tests are passing (unittest, etc.).

Reviewer Notes

Summary by CodeRabbit

  • New Features

    • Added benchmarking support for fused residual add + RMSNorm and Gemma RMSNorm variants.
    • Introduced --enable_pdl flag to enable Programmatic Dependent Launch optimization in benchmarks.
  • Documentation

    • Updated benchmark support matrix: norm routines now default to CuTe-DSL backend instead of CUDA.
    • Added PDL compatibility warnings across multiple benchmark routines.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR extends the benchmark infrastructure with Programmatic Dependent Launch (PDL) support and introduces three new RMSNorm benchmark variants. It adds a --enable_pdl CLI flag, creates a utility to warn when PDL is unsupported, shifts norm routines to CuTe-DSL backends, and systematically propagates the flag through multiple benchmark modules.

Changes

PDL Support and RMSNorm Benchmark Enhancements

Layer / File(s) Summary
PDL CLI flag and warning utility
benchmarks/flashinfer_benchmark.py, benchmarks/routines/flashinfer_benchmark_utils.py
Introduces --enable_pdl argument to the main benchmark parser and adds warn_if_pdl_unsupported(args, routine_name) helper to emit warnings when PDL is requested but not supported by a routine.
RMSNorm benchmark variants and backend routing
benchmarks/routines/norm.py
Adds three new RMSNorm test functions for fused residual add, Gemma RMSNorm, and Gemma fused variants; updates existing RMSNorm and quantized variants to default to CuTe-DSL backend; implements optional reference checks and result serialization.
Benchmark utilities: norm registry and backend support
benchmarks/routines/flashinfer_benchmark_utils.py
Registers new RMSNorm variants in the API registry, removes bmm_fp8 from routine support map, and updates compute-capability backend entries to use CuTe-DSL for all norm/RMSNorm routines.
Enable-PDL propagation to multiple benchmark routines
benchmarks/routines/attention.py, benchmarks/routines/gemm.py, benchmarks/routines/moe.py, benchmarks/routines/rope.py, benchmarks/routines/sampling.py, benchmarks/routines/allreduce_comm.py, benchmarks/routines/mamba.py, benchmarks/routines/mixed_comm.py, benchmarks/routines/moe_comm.py, benchmarks/routines/quantization.py
Threads enable_pdl=args.enable_pdl through kernel/wrapper invocations across multiple routine benchmarks; adds warn_if_pdl_unsupported() calls at test entry points to validate PDL compatibility before execution.
Documentation and sample test list updates
benchmarks/README.md, benchmarks/samples/sample_testlist.txt
Updates README to document new RMSNorm routines and CuTe-DSL backend defaults; adds sample benchmark entries for the three new RMSNorm variants and removes legacy FP8 e5m2 quantized cases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

benchmark, op: norm, op: misc

Suggested reviewers

  • cyx-6
  • yzh119
  • kahyunnam
  • jiahanc
  • Anerudhan
  • aleozlx

Poem

🐰 Hop, hop, hop—new PDL flags in flight,
RMSNorm variants now shine so bright,
CuTe-DSL kernels join the fray,
Benchmarks bloom in every way!
Enable those PDL paths with delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.51% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately and concisely summarizes the three main changes: unified PDL behavior, addition of norm routines, and misc improvements.
Description check ✅ Passed Description includes a comprehensive explanation of changes, PDL behavior unification, new benchmarks added, backend corrections, and miscellaneous fixes. However, the PR checklist sections (pre-commit and tests) have empty checkboxes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request adds benchmarks for several new normalization routines, including fused_add_rmsnorm, gemma_rmsnorm, and gemma_fused_add_rmsnorm, defaulting their backend to cute-dsl. It also introduces a global --enable_pdl flag with a warning mechanism for unsupported routines, programmatically filters backends for bmm_fp8 via trial calls, and pre-allocates scale tensors on-device to ensure compatibility with CUDA graph capture. The reviewer feedback suggests using a list comprehension instead of list.remove() for filtering backends in gemm.py, and recommends supporting 3D inputs (via num_heads) in both testFusedAddRmsnorm and testGemmaFusedAddRmsnorm to maintain consistency with other RMSNorm benchmarks.

Comment on lines +702 to +703
for backend in backends_to_remove:
backends.remove(backend)

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.

medium

Instead of iterating and calling .remove() on the list, which can be inefficient and prone to issues if there are duplicates, you can use a list comprehension to filter backends in a more Pythonic and efficient way.

    backends = [b for b in backends if b not in backends_to_remove]

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Current changes match the the pattern used by testMmFp4 and other sibling test functions in this file — backends comes from a fixed argparse choices= set so duplicates aren't possible, and the list is ~5 items. Leaving as-is for consistency.

f"Unsupported input dtype: {args.input_dtype}. Supported dtypes are bfloat16, float16."
)

input_shape = (batch_size, hidden_size)

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.

medium

To make fused_add_rmsnorm consistent with rmsnorm and gemma_rmsnorm, we should support 3D inputs by extracting num_heads from args and setting the input_shape accordingly.

Suggested change
input_shape = (batch_size, hidden_size)
num_heads = args.num_heads
if num_heads is not None:
input_shape = (batch_size, num_heads, hidden_size)
else:
input_shape = (batch_size, hidden_size)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Skipping this — flashinfer.fused_add_rmsnorm and flashinfer.gemma_fused_add_rmsnorm are 2D-only per their docstrings and impl (no if input.dim(== 3: branch like rmsnorm has, no QK fused-add variant in flashinfer/norm/). Exposing --num_heads here would let users construct 3D inputs that the underlying API doesn't actually support. If we want a 3D fused-add benchmark, the API needs to grow that support first.

f"Unsupported input dtype: {args.input_dtype}. Supported dtypes are bfloat16, float16."
)

input_shape = (batch_size, hidden_size)

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.

medium

To make gemma_fused_add_rmsnorm consistent with rmsnorm and gemma_rmsnorm, we should support 3D inputs by extracting num_heads from args and setting the input_shape accordingly.

Suggested change
input_shape = (batch_size, hidden_size)
num_heads = args.num_heads
if num_heads is not None:
input_shape = (batch_size, num_heads, hidden_size)
else:
input_shape = (batch_size, hidden_size)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ditto as above

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
benchmarks/routines/allreduce_comm.py (1)

291-299: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Honor --enable_pdl in allreduce benchmark execution.

Line 461 adds a PDL-support warning, but both benchmark paths still hardcode launch_with_pdl=True, so the CLI flag is ignored.

Suggested fix
         def run_allreduce(inp):
             allreduce_fusion(
                 input=inp,
                 workspace=workspace,
                 pattern=pattern_code,
-                launch_with_pdl=True,
+                launch_with_pdl=args.enable_pdl,
                 output=output,
                 use_oneshot=use_oneshot,
             )
             return output
@@
         def run_allreduce(inp):
             allreduce_fusion(
                 input=inp,
                 workspace=workspace,
                 pattern=pattern_code,
-                launch_with_pdl=True,
+                launch_with_pdl=args.enable_pdl,
                 residual_out=residual_out,
                 norm_out=norm_out,
                 residual_in=residual,
                 rms_gamma=norm_weight,

Also applies to: 309-320, 461-461

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@benchmarks/routines/allreduce_comm.py` around lines 291 - 299, The calls to
allreduce_fusion currently hardcode launch_with_pdl=True, ignoring the CLI flag;
update each call site (e.g., inside the run_allreduce wrapper where
allreduce_fusion is invoked and the other similar invocation blocks) to pass the
actual enable_pdl boolean flag (or the variable that holds the CLI option) as
launch_with_pdl instead of True so the benchmark honors --enable_pdl; locate and
change all occurrences (the run_allreduce function and the other
allreduce_fusion call sites noted in the diff) to use
launch_with_pdl=enable_pdl.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@benchmarks/routines/allreduce_comm.py`:
- Around line 291-299: The calls to allreduce_fusion currently hardcode
launch_with_pdl=True, ignoring the CLI flag; update each call site (e.g., inside
the run_allreduce wrapper where allreduce_fusion is invoked and the other
similar invocation blocks) to pass the actual enable_pdl boolean flag (or the
variable that holds the CLI option) as launch_with_pdl instead of True so the
benchmark honors --enable_pdl; locate and change all occurrences (the
run_allreduce function and the other allreduce_fusion call sites noted in the
diff) to use launch_with_pdl=enable_pdl.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5ecc8c26-bd2d-4cd3-9e3b-e69cbd9bea37

📥 Commits

Reviewing files that changed from the base of the PR and between 8eb6154 and c1f059a.

📒 Files selected for processing (15)
  • benchmarks/README.md
  • benchmarks/flashinfer_benchmark.py
  • benchmarks/routines/allreduce_comm.py
  • benchmarks/routines/attention.py
  • benchmarks/routines/flashinfer_benchmark_utils.py
  • benchmarks/routines/gemm.py
  • benchmarks/routines/mamba.py
  • benchmarks/routines/mixed_comm.py
  • benchmarks/routines/moe.py
  • benchmarks/routines/moe_comm.py
  • benchmarks/routines/norm.py
  • benchmarks/routines/quantization.py
  • benchmarks/routines/rope.py
  • benchmarks/routines/sampling.py
  • benchmarks/samples/sample_testlist.txt

@bkryu
bkryu merged commit ce973bb into flashinfer-ai:main May 28, 2026
31 checks passed
@bkryu
bkryu deleted the benchmark_improvements branch May 28, 2026 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants