Skip to content

feat(moe): support BF16 activations in CuTe DSL NVFP4 MoE - #4048

Merged
aleozlx merged 118 commits into
flashinfer-ai:mainfrom
zianglih:agent-cutedsl-moe-bf16-activation
Aug 7, 2026
Merged

aleozlx merged 118 commits into
flashinfer-ai:mainfrom
zianglih:agent-cutedsl-moe-bf16-activation

Conversation

@zianglih

@zianglih zianglih commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

📌 Description

@HumansAnd

Adds an explicit quant_mode="w4a16" path to the SM100 CuTe DSL NVFP4 MoE APIs.

Design walkthrough

  • Interface: Explicit quant_mode="w4a16" mirrors the existing SM12x CuTe DSL MoE quant-mode dispatch instead of inferring the path from tensor dtype. W4A4/NVFP4 remains the default; W4A16 requires BF16 x, x_sf=None, fc2_input_scale=None, and no per-token scale. The unified runner uses the existing MoEConfig.quant.variant, RoutingInputMode.PackedPrecomputed, TunableRunner, use_fused_finalize, and enable_pdl contracts.
  • Weight decode: NVFP4 E2M1 values and E4M3 scales are decoded online to BF16. CUDA 13.2+ uses direct explicit-RN BF16 PTX; earlier compilers use explicit-RN FP16 conversion/multiply followed by FP32-to-BF16 widening.
  • Weight layout: TMA loads the unchanged public packed layout into SMEM. The transform assigns contiguous complete NVFP4 blocks to two decode warpgroups and writes BF16 in MMA/TMEM layout, so each E4M3 scale is decoded once and packed reads stay contiguous. For gated GEMM1 it also places 16 up rows beside their 16 gate rows, giving each epilogue warp both operands in its 32 TMEM lanes without a global repack.
  • Shared-memory reuse: The E4M3 scale buffer is shared by its TMA and transform layouts, and gated decode uses composed views of the existing packed-weight and scale stages instead of allocating repacked copies. Decoded BF16 weights go directly to TMEM. Fused finalize reinterprets its single C stage as the contiguous reduction tile, while deterministic finalize assigns only unused SMEM to additional C stages.
  • Cache policy: Activation TMA loads use evict-last because routed activation tiles are reused across output-weight M tiles. Streaming packed-weight TMA loads use evict-first so they do not displace those reusable activations from L2.
  • Warp specialization: The 16-warp CTA uses warps 0-3 for the epilogue at 176 registers/thread; warps 4-7 for MMA, scheduling, weight TMA, and activation TMA at 80; and warps 8-15 as two four-warp weight-dequantization groups at the 128-register launch default. This exactly uses the 65,536-register CTA budget.
  • Compute: Both GEMMs use BF16 tensor-core operands and FP32 accumulation. Per-expert FP32 weight scales are applied before activation or output conversion.
  • Fusion: Existing activation normalization is reused. GEMM1 applies the same SwiGLU or ReLU2 contract in its epilogue and writes the reduced BF16 intermediate directly, so W4A16 needs no separate activation or intermediate-quantization launch.
  • Reused MoE kernels: The full path is shared moe_sort -> BF16 moe_permute -> W4A16 GEMM1 -> W4A16 GEMM2 -> optional moe_unpermute. Sorting reuses the existing DeepSeek routing implementation; permutation and deterministic combine reuse the repository's moePermuteKernel and moeUnpermuteKernel. Fused finalize also reuses moe_output_memset_inplace before GEMM2's in-kernel atomic reduction.
  • Finalize: The existing use_fused_finalize contract and True default are preserved. Fused mode applies routing weights and atomically reduces in GEMM2; deterministic mode writes expanded route rows and lets shared moe_unpermute apply top-k scales and combine them.
  • Routing, EP, and updates: Shared moe_sort produces the same expert/tile maps and EP-local metadata consumed by existing CuTe DSL MoE paths. Preallocated sort/workspace buffers preserve CUDA-graph addresses. Serving-owned 6D weight-scale views are read directly, so in-place updates remain visible.
  • CLC: GEMM1 switches from static persistent scheduling to a one-stage CLC claim pipeline only when its fused-activation problem exceeds resident cluster capacity. GEMM2 stays on the static scheduler because CLC regressed that workload.
  • PDL and graphs: Only the activation-TMA warp waits on the predecessor; scheduling, weight TMA, and dequantization can run early. GEMM1 releases GEMM2 from its weight-TMA warp, overlapping GEMM2's independent weight load/decode with GEMM1's fused epilogue. Runner-owned autotune/workspace state persists across eager tuning and CUDA graph capture.
  • Autotuning: W4A16 reuses W4A4's TunableRunner/AutoTuner, hybrid token buckets, cold-L2 profiling, per-shape can_implement filtering, and fixed fallback. The tactic key is (gemm1_tactic, gemm2_tactic); each member is ((mma_m, route_tile, 256), (cluster_m, 1), True) for (mma_tiler_mnk, cluster_shape_mn, raster_along_m), and both share route_tile in {8, 16, 32, 64, 128, 192}. Unlike W4A4's Cartesian product, W4A16 uses curated 1-CTA, 2-CTA, and mixed GEMM pairs because grouped scheduling fixes cluster N to 1 and pipeline storage excludes some tile pairs.
  • W4A8 scope: Commit 51b6e718 records a W4A8 prototype and 3e1283a8 removes it after noncompetitive results, so W4A8 is intentionally outside this PR. It can reuse the quant_mode interface later, potentially with PTX 9.4's mul.rn.e4m3x4.e2m1x4.e4m3x4.satfinite fp8x4, fp4x4, scale_x4;, a packed E2M1x4 x E4M3x4 to E4M3x4 operation on sm_100a.

Performance

Workloads

Both isolated comparisons use B200, CUDA graphs, CUPTI timing, and an EP8 shape simulation with 32 local experts. Initial activation quantization is timed for W4A4 and TRTLLM; W4A16 consumes BF16 directly.

  • Inference: per-token activation, no 4over6, FP4 quantization fast math enabled, and fused finalize enabled.
  • Deterministic RL: per-token 4over6 MSE, E4M3 max 256, 4over6 error fast math enabled, FP4 quantization fast math disabled, and fused finalize disabled.
# Inference; run with FLASHINFER_NVFP4_4OVER6* and
# FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH unset.
CUDA_VISIBLE_DEVICES=0 FLASHINFER_DISABLE_VERSION_CHECK=1 \
python3 benchmarks/bench_moe_deepseek.py \
  --num-tokens 1,2,4,8,16,32,64,128,256,512,1024,2048,4096 \
  --ep 8 --use-per-token-activation --include-activation-quant

# Deterministic RL
CUDA_VISIBLE_DEVICES=0 \
FLASHINFER_DISABLE_VERSION_CHECK=1 \
FLASHINFER_NVFP4_4OVER6=1 \
FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1 \
FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE \
FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1 \
FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1 \
python3 benchmarks/bench_moe_deepseek.py \
  --num-tokens 1,2,4,8,16,32,64,128,256,512,1024,2048,4096 \
  --ep 8 --use-per-token-activation --include-activation-quant \
  --no-fused-finalize

Speedup is TRTLLM latency divided by W4A16 latency; values above 1 favor W4A16. TRTLLM falls back to its default tactic for 1, 2, and 4 tokens because those shapes are outside its tuning buckets.

CUDA 13.2 W4A16 NSYS & NCU

image

GEMM1:
image
image
GEMM1 is mostly BF16 tensor core bound. PDL gives a 20us~30us overlap, showed by both NSYS and NCU.

GEMM2:
image
image

Pipeline utilization is lower maybe due to the early PDL overlap.

CUDA 13.0 W4A16 vs TRTLLM

Current head b4c44fea on B200 with PyTorch 2.11.0+cu130.

Inference

========================================================================================================================
DeepSeek-V3 MoE Benchmark: CuteDSL W4A4/W4A16 vs TRTLLM (EP=8, TP=1)
========================================================================================================================
Model: hidden=7168, intermediate=2048, experts=256, top_k=8
EP Config: 32 local experts (simulating 8-way parallelism)
TP Config: intermediate size 2048 (simulating 1-way parallelism)
CUDA Graph: enabled, CUPTI: enabled
Routing bias scale: 0.01 (larger values tend to create expert imbalance)
Timed initial activation quantization for FP4-activation backends: included; W4A16 consumes BF16 directly
CuteDSL finalize: atomic fused
CUTLASS omitted: it does not consume the per-token activation scale.
------------------------------------------------------------------------------------------------------------------------
Tokens |  CuteDSL W4A4   |  CuteDSL W4A16  |     TRTLLM      | Speedup vs TRTLLM  |  Winner  | Active  |     Stats
       |      ms  TFLOPS |      ms  TFLOPS |      ms  TFLOPS |     W4A4     W4A16 |          | experts | min/max/median
------------------------------------------------------------------------------------------------------------------------
     1 |   0.041     2.1 |   0.048     1.9 |   0.037     2.4 |     0.89x    0.77x |  TRTLLM  |       2 |   0/  1/   0.00
     2 |   0.052     3.4 |   0.054     3.2 |   0.047     3.7 |     0.90x    0.87x |  TRTLLM  |       4 |   0/  1/   0.00
     4 |   0.071     5.0 |   0.077     4.6 |   0.061     5.7 |     0.87x    0.80x |  TRTLLM  |       7 |   0/  1/   0.00
     8 |   0.087     8.1 |   0.101     7.0 |   0.076     9.3 |     0.87x    0.75x |  TRTLLM  |      10 |   0/  2/   0.00
    16 |   0.096    14.7 |   0.109    13.0 |   0.089    15.8 |     0.93x    0.82x |  TRTLLM  |      13 |   0/  2/   0.00
    32 |   0.129    21.8 |   0.159    17.7 |   0.116    24.3 |     0.90x    0.73x |  TRTLLM  |      21 |   0/  3/   1.00
    64 |   0.162    34.7 |   0.211    26.8 |   0.149    37.8 |     0.92x    0.71x |  TRTLLM  |      29 |   0/  5/   2.00
   128 |   0.166    67.9 |   0.217    51.9 |   0.155    72.9 |     0.93x    0.71x |  TRTLLM  |      30 |   0/  8/   3.00
   256 |   0.174   129.5 |   0.222   101.6 |   0.164   137.3 |     0.94x    0.74x |  TRTLLM  |      32 |   1/ 11/   7.00
   512 |   0.178   253.5 |   0.228   198.1 |   0.173   261.0 |     0.97x    0.76x |  TRTLLM  |      32 |   7/ 23/  14.00
  1024 |   0.187   483.2 |   0.243   371.1 |   0.182   495.2 |     0.98x    0.75x |  TRTLLM  |      32 |  17/ 42/  28.50
  2048 |   0.205   878.8 |   0.334   540.8 |   0.203   889.8 |     0.99x    0.61x |  TRTLLM  |      32 |  38/ 74/  57.50
  4096 |   0.263  1374.3 |   0.448   806.0 |   0.255  1412.1 |     0.97x    0.57x |  TRTLLM  |      32 |  81/147/ 117.00
------------------------------------------------------------------------------------------------------------------------
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend

Deterministic RL

========================================================================================================================
DeepSeek-V3 MoE Benchmark: CuteDSL W4A4/W4A16 vs TRTLLM (EP=8, TP=1)
========================================================================================================================
Model: hidden=7168, intermediate=2048, experts=256, top_k=8
EP Config: 32 local experts (simulating 8-way parallelism)
TP Config: intermediate size 2048 (simulating 1-way parallelism)
CUDA Graph: enabled, CUPTI: enabled
Routing bias scale: 0.01 (larger values tend to create expert imbalance)
Timed initial activation quantization for FP4-activation backends: included; W4A16 consumes BF16 directly
CuteDSL finalize: deterministic two-stage
CUTLASS omitted: it does not consume the per-token activation scale.
------------------------------------------------------------------------------------------------------------------------
Tokens |  CuteDSL W4A4   |  CuteDSL W4A16  |     TRTLLM      | Speedup vs TRTLLM  |  Winner  | Active  |     Stats
       |      ms  TFLOPS |      ms  TFLOPS |      ms  TFLOPS |     W4A4     W4A16 |          | experts | min/max/median
------------------------------------------------------------------------------------------------------------------------
     1 |   0.045     2.0 |   0.046     1.9 |   0.039     2.3 |     0.86x    0.84x |  TRTLLM  |       2 |   0/  1/   0.00
     2 |   0.059     3.0 |   0.054     3.3 |   0.049     3.6 |     0.83x    0.91x |  TRTLLM  |       4 |   0/  1/   0.00
     4 |   0.078     4.5 |   0.076     4.6 |   0.063     5.6 |     0.81x    0.83x |  TRTLLM  |       7 |   0/  1/   0.00
     8 |   0.094     7.5 |   0.098     7.2 |   0.077     9.1 |     0.82x    0.79x |  TRTLLM  |      10 |   0/  2/   0.00
    16 |   0.103    13.7 |   0.109    12.9 |   0.090    15.7 |     0.87x    0.82x |  TRTLLM  |      13 |   0/  2/   0.00
    32 |   0.137    20.6 |   0.159    17.7 |   0.118    23.9 |     0.86x    0.74x |  TRTLLM  |      21 |   0/  3/   1.00
    64 |   0.173    32.6 |   0.211    26.7 |   0.151    37.2 |     0.88x    0.72x |  TRTLLM  |      29 |   0/  5/   2.00
   128 |   0.176    64.0 |   0.215    52.4 |   0.156    72.3 |     0.89x    0.72x |  TRTLLM  |      30 |   0/  8/   3.00
   256 |   0.186   121.2 |   0.225   100.2 |   0.166   135.5 |     0.89x    0.74x |  TRTLLM  |      32 |   1/ 11/   7.00
   512 |   0.192   234.7 |   0.230   196.4 |   0.177   255.4 |     0.92x    0.77x |  TRTLLM  |      32 |   7/ 23/  14.00
  1024 |   0.203   444.0 |   0.244   369.9 |   0.188   480.2 |     0.92x    0.77x |  TRTLLM  |      32 |  17/ 42/  28.50
  2048 |   0.231   781.5 |   0.313   576.2 |   0.212   849.3 |     0.92x    0.68x |  TRTLLM  |      32 |  38/ 74/  57.50
  4096 |   0.305  1181.2 |   0.407   886.4 |   0.269  1338.9 |     0.88x    0.66x |  TRTLLM  |      32 |  81/147/ 117.00
------------------------------------------------------------------------------------------------------------------------
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend

CUDA 13.2 W4A16 vs TRTLLM

Current head b4c44fea on B200 with PyTorch 2.13.0+cu132.

Inference

========================================================================================================================
DeepSeek-V3 MoE Benchmark: CuteDSL W4A4/W4A16 vs TRTLLM (EP=8, TP=1)
========================================================================================================================
Model: hidden=7168, intermediate=2048, experts=256, top_k=8
EP Config: 32 local experts (simulating 8-way parallelism)
TP Config: intermediate size 2048 (simulating 1-way parallelism)
CUDA Graph: enabled, CUPTI: enabled
Routing bias scale: 0.01 (larger values tend to create expert imbalance)
Timed initial activation quantization for FP4-activation backends: included; W4A16 consumes BF16 directly
CuteDSL finalize: atomic fused
CUTLASS omitted: it does not consume the per-token activation scale.
------------------------------------------------------------------------------------------------------------------------
Tokens |  CuteDSL W4A4   |  CuteDSL W4A16  |     TRTLLM      | Speedup vs TRTLLM  |  Winner  | Active  |     Stats
       |      ms  TFLOPS |      ms  TFLOPS |      ms  TFLOPS |     W4A4     W4A16 |          | experts | min/max/median
------------------------------------------------------------------------------------------------------------------------
     1 |   0.048     1.8 |   0.048     1.9 |   0.038     2.3 |     0.79x    0.79x |  TRTLLM  |       2 |   0/  1/   0.00
     2 |   0.053     3.3 |   0.056     3.2 |   0.048     3.7 |     0.91x    0.86x |  TRTLLM  |       4 |   0/  1/   0.00
     4 |   0.072     4.9 |   0.079     4.5 |   0.062     5.7 |     0.86x    0.79x |  TRTLLM  |       7 |   0/  1/   0.00
     8 |   0.088     8.0 |   0.101     7.0 |   0.076     9.3 |     0.86x    0.75x |  TRTLLM  |      10 |   0/  2/   0.00
    16 |   0.096    14.7 |   0.109    12.9 |   0.089    15.8 |     0.93x    0.82x |  TRTLLM  |      13 |   0/  2/   0.00
    32 |   0.130    21.6 |   0.161    17.5 |   0.117    24.1 |     0.90x    0.73x |  TRTLLM  |      21 |   0/  3/   1.00
    64 |   0.164    34.4 |   0.213    26.4 |   0.149    37.9 |     0.91x    0.70x |  TRTLLM  |      29 |   0/  5/   2.00
   128 |   0.167    67.3 |   0.219    51.4 |   0.155    72.6 |     0.93x    0.71x |  TRTLLM  |      30 |   0/  8/   3.00
   256 |   0.175   128.5 |   0.225   100.4 |   0.165   136.8 |     0.94x    0.73x |  TRTLLM  |      32 |   1/ 11/   7.00
   512 |   0.179   251.3 |   0.231   195.1 |   0.174   258.6 |     0.97x    0.75x |  TRTLLM  |      32 |   7/ 23/  14.00
  1024 |   0.189   477.6 |   0.245   367.5 |   0.185   488.1 |     0.98x    0.75x |  TRTLLM  |      32 |  17/ 42/  28.50
  2048 |   0.209   863.9 |   0.313   576.1 |   0.209   863.8 |     1.00x    0.67x |   W4A4   |      32 |  38/ 74/  57.50
  4096 |   0.265  1361.1 |   0.455   793.4 |   0.258  1396.9 |     0.97x    0.57x |  TRTLLM  |      32 |  81/147/ 117.00
------------------------------------------------------------------------------------------------------------------------
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend

Deterministic RL

========================================================================================================================
DeepSeek-V3 MoE Benchmark: CuteDSL W4A4/W4A16 vs TRTLLM (EP=8, TP=1)
========================================================================================================================
Model: hidden=7168, intermediate=2048, experts=256, top_k=8
EP Config: 32 local experts (simulating 8-way parallelism)
TP Config: intermediate size 2048 (simulating 1-way parallelism)
CUDA Graph: enabled, CUPTI: enabled
Routing bias scale: 0.01 (larger values tend to create expert imbalance)
Timed initial activation quantization for FP4-activation backends: included; W4A16 consumes BF16 directly
CuteDSL finalize: deterministic two-stage
CUTLASS omitted: it does not consume the per-token activation scale.
------------------------------------------------------------------------------------------------------------------------
Tokens |  CuteDSL W4A4   |  CuteDSL W4A16  |     TRTLLM      | Speedup vs TRTLLM  |  Winner  | Active  |     Stats
       |      ms  TFLOPS |      ms  TFLOPS |      ms  TFLOPS |     W4A4     W4A16 |          | experts | min/max/median
------------------------------------------------------------------------------------------------------------------------
     1 |   0.045     1.9 |   0.046     1.9 |   0.038     2.3 |     0.84x    0.82x |  TRTLLM  |       2 |   0/  1/   0.00
     2 |   0.059     3.0 |   0.055     3.2 |   0.049     3.6 |     0.83x    0.90x |  TRTLLM  |       4 |   0/  1/   0.00
     4 |   0.078     4.5 |   0.077     4.5 |   0.063     5.6 |     0.81x    0.82x |  TRTLLM  |       7 |   0/  1/   0.00
     8 |   0.094     7.5 |   0.099     7.2 |   0.078     9.1 |     0.83x    0.79x |  TRTLLM  |      10 |   0/  2/   0.00
    16 |   0.102    13.8 |   0.109    12.9 |   0.090    15.7 |     0.88x    0.82x |  TRTLLM  |      13 |   0/  2/   0.00
    32 |   0.138    20.5 |   0.161    17.5 |   0.118    23.8 |     0.86x    0.74x |  TRTLLM  |      21 |   0/  3/   1.00
    64 |   0.174    32.5 |   0.214    26.4 |   0.152    37.1 |     0.88x    0.71x |  TRTLLM  |      29 |   0/  5/   2.00
   128 |   0.177    63.7 |   0.217    52.0 |   0.157    71.8 |     0.89x    0.72x |  TRTLLM  |      30 |   0/  8/   3.00
   256 |   0.187   120.3 |   0.224   100.8 |   0.168   134.6 |     0.89x    0.75x |  TRTLLM  |      32 |   1/ 11/   7.00
   512 |   0.192   234.3 |   0.229   196.6 |   0.178   252.9 |     0.93x    0.78x |  TRTLLM  |      32 |   7/ 23/  14.00
  1024 |   0.205   440.1 |   0.245   368.0 |   0.189   476.5 |     0.92x    0.77x |  TRTLLM  |      32 |  17/ 42/  28.50
  2048 |   0.235   768.9 |   0.314   575.3 |   0.213   845.7 |     0.91x    0.68x |  TRTLLM  |      32 |  38/ 74/  57.50
  4096 |   0.310  1164.2 |   0.409   882.6 |   0.273  1319.4 |     0.88x    0.67x |  TRTLLM  |      32 |  81/147/ 117.00
------------------------------------------------------------------------------------------------------------------------
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend

CUDA 13.0 end-to-end multi-GPU RL workload

Current head b4c44fea on 8x B200, PyTorch 2.11.0+cu130, CuTe DSL compiler 13.1, CUPTI 13.0.85, and NCCL 2.30.7.

  • W4A4 EP8 is the baseline and includes per-token NVFP4 4over6 MSE quantization plus deterministic finalize.
  • W4A16 starts from BF16 and does not launch activation quantization.
  • EP includes dispatch/combine; TP includes all-gather/all-reduce.
  • Each cell is the median of three runs; each run uses 10 warmups and 100 maximum-rank CUDA-event samples with an L2 flush.
  • Speedup is W4A4 EP8 latency divided by strategy latency.
FLASHINFER_DISABLE_VERSION_CHECK=1 \
FLASHINFER_NVFP4_4OVER6=1 \
FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1 \
FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE \
FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1 \
FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1 \
torchrun --standalone --nproc-per-node=8 \
  benchmarks/bench_cute_dsl_moe_distributed.py \
  --warmup 10 --iters 100 \
  --use-per-token-activation --no-fused-finalize
Global tokens W4A4 EP8 baseline (ms) W4A4 TP8 (ms) Speedup W4A16 EP8 (ms) Speedup W4A16 TP8 (ms) Speedup
1 0.334 0.346 0.966x 0.223 1.501x 0.226 1.482x
2 0.362 0.366 0.989x 0.255 1.419x 0.231 1.568x
4 0.375 0.350 1.071x 0.286 1.312x 0.240 1.558x
8 0.380 0.369 1.032x 0.318 1.196x 0.266 1.431x
16 0.385 0.392 0.983x 0.345 1.115x 0.305 1.264x
32 0.406 0.441 0.919x 0.382 1.062x 0.360 1.127x
64 0.428 0.461 0.928x 0.410 1.045x 0.401 1.068x
128 0.437 0.485 0.900x 0.420 1.041x 0.429 1.017x
256 0.441 0.497 0.888x 0.433 1.020x 0.448 0.984x
512 0.449 0.512 0.875x 0.441 1.017x 0.468 0.958x
1024 0.464 0.546 0.850x 0.483 0.962x 0.546 0.851x
2048 0.494 0.642 0.770x 0.599 0.825x 0.762 0.649x
4096 0.578 0.887 0.651x 0.751 0.769x 1.233 0.469x
Geomean / min / max 0.903x / 0.651x / 1.071x 1.080x / 0.769x / 1.501x 1.053x / 0.469x / 1.568x

CUDA 13.0 Nsight Systems RL breakdown

Nsight Systems 2026.1.3 captured 20 iterations for each EP8/TP8 and W4A4/W4A16 case. Values are aggregate staged kernel time divided by 8 ranks x 20 iterations; concurrent kernels are not removed.

FLASHINFER_DISABLE_VERSION_CHECK=1 \
FLASHINFER_NVFP4_4OVER6=1 \
FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1 \
FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE \
FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1 \
FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1 \
python benchmarks/bench_cute_dsl_moe_distributed.py \
  --mode profile_nsys --warmup 10 --profile-iters 20 \
  --use-per-token-activation --no-fused-finalize \
  --nsys-output-dir /tmp/cute_dsl_moe_nsys_cuda130

32 global tokens

Operation W4A4 EP8 W4A16 EP8 W4A4 TP8 W4A16 TP8
Input communication 0.022 0.022 0.023 0.027
Routing 0.003 0.003 0.003 0.003
Input activation quantization 0.006 - 0.005 -
Route indices 0.004 0.004 0.004 0.004
Permute - 0.004 - 0.006
GEMM1 0.071 0.093 0.072 0.097
Activation 0.001 - 0.001 -
Intermediate quantization 0.008 - 0.036 -
GEMM2 0.032 0.068 0.044 0.077
Unpermute 0.009 0.008 0.020 0.014
Output communication 0.067 0.073 0.029 0.032
Total staged GPU kernel time 0.223 0.276 0.239 0.260

4096 global tokens

Operation W4A4 EP8 W4A16 EP8 W4A4 TP8 W4A16 TP8
Input communication 0.073 0.082 0.138 0.157
Routing 0.003 0.003 0.009 0.009
Input activation quantization 0.024 - 0.033 -
Route indices 0.007 0.007 0.009 0.011
Permute - 0.021 - 0.144
GEMM1 0.108 0.233 0.126 0.308
Activation 0.003 - 0.001 -
Intermediate quantization 0.064 - 0.076 -
GEMM2 0.061 0.184 0.156 0.322
Unpermute 0.031 0.031 0.106 0.103
Output communication 0.094 0.107 0.182 0.182
Total staged GPU kernel time 0.469 0.669 0.835 1.236

CUDA 13.2 end-to-end multi-GPU RL workload

Current head b2442579 on 8x B200, PyTorch 2.13.0+cu132, CuTe DSL 4.6.1, CUPTI 13.2.75, and NCCL 2.29.7.

W4A4 EP8 is the baseline and includes per-token NVFP4 4over6 MSE quantization plus deterministic finalize; W4A16 starts from BF16. EP includes dispatch/combine, and TP includes all-gather/all-reduce. Each cell is the median of three runs with 10 warmups and 100 maximum-rank CUDA-event samples; speedup is W4A4 EP8 latency divided by strategy latency.

FLASHINFER_DISABLE_VERSION_CHECK=1 \
FLASHINFER_NVFP4_4OVER6=1 \
FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1 \
FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE \
FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1 \
FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1 \
torchrun --standalone --nproc-per-node=8 \
  benchmarks/bench_cute_dsl_moe_distributed.py \
  --warmup 10 --iters 100 \
  --use-per-token-activation --no-fused-finalize
Global tokens W4A4 EP8 baseline (ms) W4A4 TP8 (ms) Speedup W4A16 EP8 (ms) Speedup W4A16 TP8 (ms) Speedup
1 0.369 0.376 0.983x 0.251 1.471x 0.250 1.478x
2 0.378 0.373 1.014x 0.260 1.450x 0.251 1.503x
4 0.384 0.377 1.020x 0.291 1.318x 0.255 1.505x
8 0.382 0.393 0.974x 0.321 1.191x 0.283 1.351x
16 0.387 0.397 0.975x 0.350 1.104x 0.320 1.210x
32 0.409 0.438 0.932x 0.387 1.055x 0.372 1.098x
64 0.431 0.470 0.916x 0.418 1.032x 0.411 1.048x
128 0.441 0.491 0.897x 0.428 1.029x 0.444 0.993x
256 0.447 0.500 0.894x 0.439 1.020x 0.464 0.963x
512 0.455 0.515 0.884x 0.453 1.004x 0.489 0.931x
1024 0.468 0.566 0.827x 0.506 0.925x 0.575 0.814x
2048 0.503 0.661 0.761x 0.624 0.806x 0.779 0.645x
4096 0.589 0.887 0.664x 0.808 0.729x 1.240 0.475x
Geomean / min / max 0.897x / 0.664x / 1.020x 1.066x / 0.729x / 1.471x 1.026x / 0.475x / 1.505x

CUDA 13.2 Nsight Systems RL breakdown

Nsight Systems 2025.6.3 captured 20 iterations for each EP8/TP8 and W4A4/W4A16 case. Values are aggregate staged kernel time divided by 8 ranks x 20 iterations; concurrent kernels are not removed.

FLASHINFER_DISABLE_VERSION_CHECK=1 \
FLASHINFER_NVFP4_4OVER6=1 \
FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1 \
FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE \
FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1 \
FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1 \
python3 benchmarks/bench_cute_dsl_moe_distributed.py \
  --mode profile_nsys --warmup 10 --profile-iters 20 \
  --use-per-token-activation --no-fused-finalize \
  --nsys-output-dir /tmp/cute_dsl_moe_nsys_cuda132

32 global tokens

Operation W4A4 EP8 W4A16 EP8 W4A4 TP8 W4A16 TP8
Input communication 0.041 0.025 0.025 0.043
Routing 0.003 0.003 0.003 0.003
Input activation quantization 0.006 - 0.006 -
Route indices 0.004 0.004 0.004 0.004
Permute - 0.004 - 0.006
GEMM1 0.072 0.094 0.074 0.099
Activation 0.001 - 0.001 -
Intermediate quantization 0.008 - 0.036 -
GEMM2 0.032 0.068 0.039 0.073
Unpermute 0.009 0.008 0.019 0.014
Output communication 0.090 0.076 0.090 0.064
Total staged GPU kernel time 0.266 0.282 0.296 0.305

4096 global tokens

Operation W4A4 EP8 W4A16 EP8 W4A4 TP8 W4A16 TP8
Input communication 0.068 0.069 0.172 0.152
Routing 0.003 0.003 0.008 0.009
Input activation quantization 0.024 - 0.033 -
Route indices 0.008 0.008 0.008 0.011
Permute - 0.022 - 0.143
GEMM1 0.109 0.235 0.124 0.309
Activation 0.003 - 0.001 -
Intermediate quantization 0.063 - 0.074 -
GEMM2 0.061 0.184 0.142 0.319
Unpermute 0.034 0.034 0.117 0.115
Output communication 0.105 0.112 0.208 0.181
Total staged GPU kernel time 0.478 0.667 0.887 1.240

🔍 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.).

Coverage includes functional and wrapper APIs, W4A4/W4A16, SwiGLU/ReLU2, PDL on/off, both finalize modes, EP1/EP8, tactic boundaries, CUDA graphs, in-place weight-scale updates, and the EP compute bridge.

CUDA_VISIBLE_DEVICES=0 FLASHINFER_DISABLE_VERSION_CHECK=1 \
python3 -m pytest -q tests/moe/test_cute_dsl_fused_moe.py
# CUDA 13.0 / CuTe DSL compiler 13.1: 517 passed

CUDA_VISIBLE_DEVICES=0 FLASHINFER_DISABLE_VERSION_CHECK=1 \
python3 -m pytest -q \
  tests/moe_ep/test_compute_bridge.py \
  tests/moe/test_unified_moe.py::TestMoERunnerSupport
# 56 passed

pre-commit run --all-files
# passed

Reviewer Notes

  • CUDA 13.2+ selects direct explicit-RN E2M1/E4M3-to-BF16 PTX. Earlier compilers use the explicit-RN FP16 compatibility path.
  • CUDA graph users run one eager call under autotune(True) before capture; capture then reuses the selected tactic and runner-owned workspace.
  • The shared CUDA permutation binding receives the current PyTorch stream because W4A16 reuses that utility inside the CuTe DSL graph-captured path.
  • W4A16 reads serving-owned 6D weight scales directly; it does not cache a transformed copy.

Summary by CodeRabbit

  • New Features

    • Added W4A16 fused MoE execution with BF16 activations and NVFP4 weights, alongside W4A4.
    • Added distributed MoE benchmarking with expert- and tensor-parallel comparisons and profiler reporting.
  • Improvements

    • Enhanced activation quantization, per-token scaling, fused finalization, and CUDA stream handling.
    • Extended profiling controls, tracing, and validation for both quantization modes.
  • Tests

    • Expanded correctness, routing, tactic, API, device, and distributed execution coverage.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3347d883-4bc8-4640-ad15-6c98d0724f56

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds CuTe DSL W4A16 MoE execution with NVFP4 weight decoding, mode-aware APIs, activation-pack plumbing, stream-aware permutation dispatch, expanded tests, and distributed EP/TP profiling benchmarks. Existing W4A4 paths gain explicit quantization-mode and per-token-scale handling.

Changes

W4A16 CuTe DSL MoE

Layer / File(s) Summary
Activation-pack and runner contracts
flashinfer/fused_moe/api.py, flashinfer/fused_moe/runners.py, flashinfer/moe_ep/..., flashinfer/trace/templates/moe.py
Adds W4A16 support, optional per-token activation scales, mode-specific input packing, and updated trace schemas.
W4A16 decode and grouped GEMM
flashinfer/fused_moe/cute_dsl/blackwell/*, flashinfer/fused_moe/cute_dsl/tuner.py
Adds NVFP4-to-BF16 conversion, persistent grouped GEMM execution, workspace and kernel caching, fused finalization, and tactic validation.
Quantization-mode API dispatch
flashinfer/fused_moe/cute_dsl/fused_moe.py, flashinfer/fused_moe/cute_dsl/moe_utils.py, csrc/moe_utils_binding.cu
Dispatches functional and wrapper APIs between W4A4 and W4A16 paths and propagates explicit CUDA streams through permutation bindings.
Validation coverage
tests/moe/*, tests/moe_ep/*
Extends numerical, tactic, CUDA-graph, EP, API-consistency, support, device, and activation-pack tests.
Benchmark and profiler workflows
benchmarks/bench_cute_dsl_moe_distributed.py, benchmarks/bench_moe_deepseek.py
Adds distributed EP/TP benchmarking and W4A4/W4A16 timing, CUDA profiling, activation-quantization controls, and reporting.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: op: gemm

Suggested reviewers: anerudhan, bkryu, jiahanc

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 49.43% 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
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.
Title check ✅ Passed The title clearly summarizes the primary change: adding BF16 activation support to the CuTe DSL NVFP4 MoE path.
Description check ✅ Passed The description covers the implementation, related issue, testing, performance data, reviewer notes, and completed checklist items.
✨ 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.

@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 introduces support for BF16 activations with online NVFP4 weight dequantization and a deterministic two-stage finalize mode (non-fused) in the CuTe DSL MoE implementation for Blackwell GPUs. It updates the underlying kernels, bindings, host preparation, and tests to support these new execution paths. The review feedback recommends adding data type validation for moe_output in the BF16 activation path to ensure it is torch.bfloat16 and prevent potential runtime issues.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread flashinfer/fused_moe/cute_dsl/fused_moe.py Outdated
@zianglih

Copy link
Copy Markdown
Contributor Author

E2M1/E4M3 to BF16 compatibility experiment

I evaluated whether the CUDA 13.2-only direct conversion sequence could be replaced by the older SM120 W4A16 bit-manipulation design without losing performance.

Designs compared

The current path decodes each packed pair with:

cvt.rn.bf16x2.e2m1x2
cvt.rn.bf16x2.e4m3x2
mul.rn.bf16x2

I tested two older-toolkit-compatible alternatives:

  1. E2M1/E4M3 to FP16, packed FP16 multiply, FP16 to FP32, then packed FP32 to BF16. This was numerically correct but approximately 19-25% slower across the sweep.
  2. The SM120 W4A16 bit-manipulation contract. This included the full design rather than only substituting a local helper:
    • one-time static-weight scale preconditioning;
    • matching global-scale rebias, including the dynamic power-of-two scale factor;
    • packed prmt, mask, shift, and BF16 multiply operations;
    • eight E2M1 values decoded per helper to amortize scale handling;
    • a direct vector bitcast to consume packed 32-bit FP4 words without reconstructing them from individual bytes.

Scale preprocessing and rebasing were cached and occurred outside the measured kernel execution.

Correctness

The final packed bit-manipulation implementation passed the existing BF16 activation numerical coverage:

CUDA_VISIBLE_DEVICES=0 \
FLASHINFER_DISABLE_VERSION_CHECK=1 \
python3 -m pytest -q \
  tests/moe/test_cute_dsl_fused_moe.py::TestCuteDslMoeBf16Activation::test_numerical_accuracy

Result: 2 passed.

Performance

Environment:

  • NVIDIA B200
  • CUDA 13.2
  • PyTorch 2.13.0+cu132
  • CUPTI timing enabled
  • three independent benchmark runs per design; table reports the median

Command:

CUDA_VISIBLE_DEVICES=0 \
FLASHINFER_DISABLE_VERSION_CHECK=1 \
python3 benchmarks/bench_moe_deepseek.py \
  --warmup 10 \
  --iters 100 \
  --cute-dsl-only \
  --use-bf16-activation
Tokens Direct BF16 PTX Packed bit manipulation Bit-path slowdown
128 2.022 ms 2.563 ms 26.8%
256 2.443 ms 2.987 ms 22.3%
512 2.453 ms 2.764 ms 12.7%
1024 2.494 ms 3.041 ms 21.9%
2048 2.623 ms 3.071 ms 17.1%
4096 3.848 ms 4.352 ms 13.1%

The packed bit path reaches 0.841x geomean performance, corresponding to an 18.9% geomean latency regression. Even after matching the SM120 packing strategy, the extra integer permutations, masks, and shifts remain visible in this transform-heavy kernel.

Decision

This is not iso-performance, so I reverted the experiment and kept the CUDA 13.2 direct E2M1/E4M3-to-BF16 instructions. The PR source remains unchanged by this experiment. Supporting an older toolkit through the SM120-style decode would impose a material runtime cost rather than being a compatibility-only implementation choice.

@zianglih

Copy link
Copy Markdown
Contributor Author

Follow-up on the final W4A16 transform-loop cleanup:

  • Removed the redundant transform-warp named barrier and widened transform fragments from 64 to 128 values.
  • CUDA 13.2 matched A/B: 1.018x geomean speedup across 128-4096 tokens.
  • CUDA 13.0 compatibility-path matched A/B: 1.027x geomean speedup, with every scanned shape improving (1.017x-1.033x).
  • CUDA 13.0 numerical validation: TestCuteDslMoeBf16Activation::test_numerical_accuracy passed all four cases.

The CUDA 13.0 rerun used --cute-dsl-only and FLASHINFER_DISABLE_JIT=1, with already-built setup utilities direct-loaded. Process checks before and after measurement showed no compiler process, and no TRTLLM MoE backend module was compiled or executed. The prebuilt FP4 utility was used only to prepare static weights outside the measured interval.

@zianglih

Copy link
Copy Markdown
Contributor Author

Follow-up on the sparse W4A16 topology audit:

The retained design uses three route-density tiers: N=32 through 16 routed rows/expert, N=64 through 32 rows/expert, and N=128 above that. Sparse and medium DeepSeek GEMM1 shapes use independent 1-SM 128xN MMAs in a 2x1 cluster while retaining the existing swizzled TMA multicast for the shared BF16 activation. Dense shapes keep the existing 2-SM 256x128 MMA.

B200, CUDA 13.2, CUPTI enabled, PDL enabled, 10 warmups, 100 iterations, three-run medians:

Tokens Prior fused (ms) Current fused (ms) Speedup Prior deterministic (ms) Current deterministic (ms) Speedup
128 1.699 1.646 1.032x 1.731 1.655 1.046x
256 1.760 1.707 1.031x 1.792 1.714 1.046x
512 1.777 1.711 1.039x 1.794 1.716 1.045x
1024 1.788 1.779 1.005x 1.816 1.803 1.007x
2048 2.243 2.231 1.005x 2.115 2.118 0.999x
4096 3.321 3.320 1.000x 3.071 3.068 1.001x
Geomean 1.019x 1.024x

The mixed-load alternatives were not retained:

  • Duplicate per-CTA cp.async activation loads were approximately neutral with N=32 and slower with N=64.
  • Loading once with cp.async and forwarding the physical swizzled stage through DSM was numerically correct, but generation latency regressed 27%-41% because the peer copy serialized behind local-load completion.
  • A four-CTA TMA-multicast cluster regressed memory-bound cases.
  • Direct descriptor-free global bulk copy cannot replace TMA here because it does not produce the swizzled shared layout required by the tcgen05 B fragment.

Validation on the final code:

CUDA_VISIBLE_DEVICES=0 pytest -q \
  tests/moe/test_cute_dsl_fused_moe.py::TestCuteDslMoeBf16Activation
# 8 passed

pre-commit run --all-files
# passed

@zianglih

Copy link
Copy Markdown
Contributor Author

CUDA 13.0 results have been refreshed on the current PR head (3e234ae4).

Environment: B200, PyTorch 2.11.0+cu130, CUDA 13.0, CuTe DSL compiler 13.1, CUPTI 13.0.85, PDL enabled, CUDA Graph enabled, 10 warmups, and 100 measured iterations. Each value is the median of three independent runs; all nine runs reported CUPTI: enabled.

The W4A4 baseline starts from BF16 and includes per-token NVFP4 4over6 MSE quantization, with E4M3 max 256, FP4 quantization fast math disabled, 4over6 error fast math disabled, and deterministic two-stage finalize. W4A16 consumes BF16 directly without activation quantization.

Tokens W4A4 per-token 4over6 + quant, deterministic (ms) W4A16, fused (ms) Fused slowdown W4A16, deterministic (ms) Deterministic slowdown
128 1.062 2.117 1.993x 2.125 2.001x
256 1.109 2.194 1.978x 2.201 1.985x
512 1.143 2.198 1.923x 2.205 1.929x
1024 1.191 2.269 1.905x 2.276 1.911x
2048 1.331 2.450 1.841x 2.344 1.761x
4096 1.863 3.738 2.006x 3.467 1.861x
Geomean 1.940x 1.906x

Compatibility-path correctness was rerun across the full focused matrix:

tests/moe/test_cute_dsl_fused_moe.py::TestCuteDslMoeBf16Activation
8 passed

The PR body now uses these current post-finalize-refactor CUDA 13.0 results instead of the stale pre-refactor headline table.

@ziang-and
ziang-and force-pushed the agent-cutedsl-moe-bf16-activation branch from 9c5f115 to 6dd1ed7 Compare July 20, 2026 22:16
@zianglih

Copy link
Copy Markdown
Contributor Author

W4A8 prototype: design and performance result

I preserved the complete W4A8 experiment in one commit and then reverted it:

  • bf6f5a4b: complete CuTe DSL W4A8 implementation, tests, benchmark support, and retained optimizations.
  • 6dbe15e7: revert the prototype so this PR has no net W4A8 source changes.

The implementation was numerically functional, and the TMA work materially improved the first version. However, online NVFP4-to-MXFP8 weight conversion still made the final W4A8 strategy substantially slower than W4A4 plus activation quantization. I therefore do not think this implementation should ship in its current form.

Prototype contract

  • Added explicit quant_mode="w4a8" dispatch to the functional API, wrapper, and runner. The activation input was MXFP8 E4M3 plus linear UE8M0 scales; the weights retained the existing NVFP4 E2M1, E4M3 block-scale, and per-expert FP32-scale contract.
  • Both grouped GEMMs loaded packed NVFP4 weights, decoded each 16-value E2M1 x E4M3 block to BF16 with explicit round-to-nearest instructions, applied the FP32 per-expert scale, and requantized complete 32-value blocks to MXFP8. The tensor core then executed MXFP8 x MXFP8 block-scaled MMA with FP32 accumulation.
  • The direct CUDA 13.2 path used cvt.rn.bf16x2.e2m1x2, cvt.rn.bf16x2.e4m3x2, and mul.rn.bf16x2. The compatibility path used explicit-RN FP16 decode/multiply and exact widening through FP32 to BF16 before MXFP8 quantization.
  • GEMM1 fused the requested activation and MXFP8 quantization in its epilogue. This avoided a standalone intermediate MXFP8 quantization launch before GEMM2. GEMM2 produced BF16 and supported both atomic fused finalize and deterministic expanded output plus moe_unpermute.
  • The launcher supported PDL, functional and wrapper APIs, EP local-expert filtering, TP, in-place weight-scale updates, and the existing workspace/autotuner structure.
  • The final 1-CTA search space had three paired GEMM1/GEMM2 tactics at route tile 128 and MMA tile 128x128: CP-async raster-N, CP-async raster-M, and TMA activation loading.

Activation and weight pipelines

The first version followed the W4A4 activation path: no standalone permutation, four cp.async warps gathered routed MXFP8 activation rows and their linear UE8M0 scales, while packed NVFP4 weights were loaded with TMA.

The optimized tactic added a separate route permutation and scale-aware permutation. It wrote activation scales directly into the tensor-core 128x4 swizzled layout, then loaded activation data and scales with TMA. This reduced the activation producer to one warp and left the remaining load warps idle. The final version also loaded packed NVFP4 data and E4M3 scales with TMA on one shared transaction barrier, removing per-fragment global scale-index calculations from the transform warps. Four transform warps received 224 registers each for NVFP4 decode, FP32 scaling, and MXFP8 requantization.

Optimization experiments

Speedup is baseline latency divided by experiment latency; values above 1.0 are improvements. These are complete 1-4096-token EP8 and TP8 sweeps.

Experiment EP8 geomean TP8 geomean Result
Activation TMA versus CP-async 1.067x 1.055x Small improvement; TMA activation tactic retained for further work.
Activation + weight-scale TMA versus CP-async 1.391x 1.304x Clear improvement; this became the final 1-CTA implementation.
Final 2-CTA implementation versus final 1-CTA TMA 0.914x 0.928x Regressed every scanned shape; rejected.
2-CTA machinery with the 2-CTA tactic removed versus final 1-CTA TMA 0.918x 0.925x Confirmed the regression came from shared pipeline changes, not autotune selection.
Restored combined-pipeline 1-CTA versus the earlier final 1-CTA run 1.003x 1.002x Recovered the original result within run-to-run noise.

Other configurations evaluated during this work included raster-M versus raster-N scheduling, 224 versus 240 transform registers, CP-async/TMA GEMM1-GEMM2 mixtures, and larger candidate tactic sets. They were numerically valid but did not improve the full sweep enough to retain. The final autotuner was deliberately reduced to the three tactics above.

The 2-CTA path was made strictly numerically correct before performance evaluation. Correct block-scaled MMA required the NVFP4 E4M3 scale fragments to be available to both peer CTAs. The working design replicated the required input/scale state and split packed weight data and weight scales into separate TMA pipelines/barriers. That extra synchronization and pipeline traffic reduced both EP and TP performance by roughly 7-9%. Removing only the 2-CTA tactic did not recover performance; restoring the 1-CTA combined data/scale TMA transaction did. The 2-CTA implementation was therefore removed from the squashed prototype.

Validation

The retained 1-CTA prototype passed:

tests/moe/test_cute_dsl_fused_moe.py::TestCuteDslMoeW4A8
tests/moe/test_cute_dsl_fused_moe.py::TestCuteDslMoeBf16Activation
44 passed

W4A8 coverage included functional and wrapper APIs, PDL on/off, fused/deterministic finalize, EP1 and EP8 local-expert routing, SwiGLU and ReLU2, in-place weight-scale updates, and every retained autotune tactic. The isolated strict 2-CTA case, the full W4A8 class, and the combined W4A8/W4A16 selection also passed before the 2-CTA performance result was rejected. pre-commit run --all-files passed.

End-to-end benchmark

Environment: 8x B200, PyTorch 2.13.0+cu132, CUDA 13.2, CuTe DSL compiler 13.3, CUPTI available, and deterministic finalize. Each row is the median of 100 maximum-rank CUDA-event samples after 10 warmups, with an L2 flush before each sample. EP8 includes real dispatch and combine. TP8 starts from the same distributed logical token set and includes activation all-gather and output all-reduce.

W4A4 starts from BF16 and includes CuTe DSL per-token NVFP4 4over6 quantization. W4A8 starts from BF16 and includes CuTe DSL MXFP8 activation quantization. The benchmark command was:

FLASHINFER_NVFP4_4OVER6=1 \
FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1 \
FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE \
FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1 \
FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1 \
torchrun --standalone --nproc_per_node=8 \
  benchmarks/bench_cute_dsl_moe_distributed.py \
  --num-gpus 8 \
  --use-per-token-activation \
  --no-fused-finalize

All slowdown columns below use W4A4 EP8 as the strategy baseline, matching the existing distributed comparison.

Global tokens W4A4 EP8 baseline (ms) W4A8 EP8 (ms) Slowdown W4A8 TP8 (ms) Slowdown
1 0.364 0.350 0.962x 0.343 0.943x
2 0.368 0.477 1.298x 0.359 0.977x
4 0.373 0.603 1.616x 0.382 1.023x
8 0.373 0.727 1.950x 0.533 1.429x
16 0.386 0.853 2.213x 0.691 1.792x
32 0.411 0.986 2.401x 0.940 2.287x
64 0.430 1.120 2.601x 1.109 2.577x
128 0.443 1.128 2.548x 1.251 2.826x
256 0.450 1.141 2.537x 1.257 2.795x
512 0.457 1.138 2.492x 1.269 2.779x
1024 0.473 1.141 2.411x 1.309 2.767x
2048 0.505 1.167 2.312x 1.394 2.761x
4096 0.585 1.766 3.017x 2.114 3.611x
Geomean / min / max 2.092x / 0.962x / 3.017x 2.008x / 0.943x / 3.611x

For a same-topology comparison, W4A8/W4A4 is 2.092x geomean under EP8 and 1.804x under TP8. The W4A16 controls in the same run were 0.913x EP8 and 0.989x TP8 relative to the W4A4 EP8 baseline.

Nsight Systems breakdown

The profiler used the same numerical contract and default 32/4096-token cases:

FLASHINFER_NVFP4_4OVER6=1 \
FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1 \
FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE \
FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1 \
FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1 \
python3 benchmarks/bench_cute_dsl_moe_distributed.py \
  --mode profile_nsys \
  --num-gpus 8 \
  --use-per-token-activation \
  --no-fused-finalize

Values are mean staged GPU-kernel time per rank per captured iteration in milliseconds, obtained by dividing each Nsight aggregate by 8 ranks x 10 iterations. They are diagnostic kernel-time sums, not application wall-clock spans, so concurrent communication is not subtracted.

32 global tokens:

Operation W4A4 EP8 W4A8 EP8 W4A4 TP8 W4A8 TP8
Input communication 0.025 0.027 0.026 0.027
Routing 0.003 0.003 0.003 0.003
Input activation quantization 0.006 0.002 0.005 0.002
Route indices 0.004 0.004 0.004 0.004
Permute - 0.005 - 0.011
GEMM1 0.072 0.387 0.074 0.405
Activation 0.001 - 0.001 -
Intermediate quantization 0.008 - 0.036 -
GEMM2 0.032 0.294 0.038 0.363
Unpermute 0.009 0.038 0.019 0.084
Output communication 0.097 0.193 0.058 0.092
Total staged GPU kernel time 0.257 0.952 0.265 0.991

4096 global tokens:

Operation W4A4 EP8 W4A8 EP8 W4A4 TP8 W4A8 TP8
Input communication 0.070 0.062 0.149 0.153
Routing 0.003 0.003 0.008 0.008
Input activation quantization 0.024 0.014 0.033 0.015
Route indices 0.007 0.008 0.008 0.010
Permute - 0.014 - 0.086
GEMM1 0.110 0.855 0.126 0.890
Activation 0.003 - 0.001 -
Intermediate quantization 0.063 - 0.074 -
GEMM2 0.061 0.571 0.142 0.764
Unpermute 0.034 0.085 0.117 0.293
Output communication 0.115 0.230 0.251 0.207
Total staged GPU kernel time 0.490 1.843 0.909 2.426

The breakdown makes the limiting factor clear. W4A8 GEMM1 plus GEMM2 account for 71-78% of its staged time in three of the four profiles and 68% in TP8 at 4096 tokens. At 4096 EP8, removing W4A4's two activation-quantization stages saves only 0.073 ms, while W4A8's two online-conversion GEMMs add 1.255 ms relative to W4A4's GEMMs. Communication and standalone quantization are not the main gap; the SIMT NVFP4 -> BF16 -> MXFP8 conversion in both GEMM pipelines is.

The TMA work reduced data movement and scale-addressing overhead substantially, but it did not remove that conversion cost. A future W4A8 design would need a materially different weight-conversion contract or substantially better overlap between conversion and MMA, rather than additional launch-configuration tuning around this kernel structure.

@zianglih

Copy link
Copy Markdown
Contributor Author

W4A16 FP32 weight-scale placement ablation

I tested the alternative contract of fully dequantizing each NVFP4 weight to BF16 in the transform/prologue instead of applying the per-expert FP32 scale to the FP32 accumulator in the epilogue.

The two matched implementations were:

  • Current epilogue contract: local_weight_bf16 = BF16(E2M1 * E4M3), followed by BF16 tensor-core GEMM with FP32 accumulation and acc_f32 *= expert_scale_f32 in the epilogue.
  • Prologue ablation: full_weight_bf16 = BF16(FP32(local_weight_bf16) * expert_scale_f32), using explicit mul.rn.f32 and cvt.rn.bf16x2.f32, followed by GEMM with no expert-scale multiplication in either epilogue.

The ablation was an internal compile-time switch, was included in the compiled-kernel cache key, and independently ran the existing W4A16 autotuner. It did not change the public API, routing, communication, tactics, PDL, activation fusion, or finalize algorithm.

Setup

  • 8x B200, CUDA 13.2, PyTorch 2.13.0+cu132, CuTe DSL compiler 13.3
  • Real EP8 and TP8 communication over the full 1-4096 global-token sweep
  • Median of 100 maximum-rank CUDA-event samples after 10 warmups, with cold-L2 flushing
  • Deterministic finalize (--no-fused-finalize), PDL enabled
  • W4A4 control: per-token NVFP4 4over6 MSE, E4M3 max 256, quantization fast math disabled, 4over6 error fast math enabled
FLASHINFER_DISABLE_VERSION_CHECK=1 \
FLASHINFER_NVFP4_4OVER6=1 \
FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1 \
FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE \
FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1 \
FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1 \
torchrun --standalone --nproc-per-node=8 \
  benchmarks/bench_cute_dsl_moe_distributed.py \
  --warmup 10 --iters 100 \
  --use-per-token-activation --no-fused-finalize

The prologue run adds FLASHINFER_W4A16_SCALE_WEIGHTS_IN_PROLOGUE=1; the epilogue baseline omits it.

Global tokens EP8 epilogue (ms) EP8 prologue (ms) EP8 prologue / epilogue TP8 epilogue (ms) TP8 prologue (ms) TP8 prologue / epilogue
1 0.257 0.253 0.985x 0.256 0.256 0.998x
2 0.260 0.299 1.147x 0.257 0.254 0.989x
4 0.280 0.347 1.240x 0.260 0.265 1.021x
8 0.303 0.393 1.298x 0.287 0.330 1.150x
16 0.329 0.441 1.337x 0.330 0.389 1.178x
32 0.359 0.487 1.359x 0.383 0.483 1.262x
64 0.383 0.537 1.403x 0.414 0.543 1.309x
128 0.413 0.549 1.329x 0.446 0.593 1.328x
256 0.421 0.555 1.317x 0.450 0.602 1.336x
512 0.413 0.567 1.374x 0.473 0.612 1.294x
1024 0.494 0.577 1.166x 0.572 0.661 1.156x
2048 0.603 0.637 1.056x 0.749 0.854 1.140x
4096 0.768 0.875 1.140x 1.196 1.320 1.104x
Geomean / min / max 1.236x / 0.985x / 1.403x 1.168x / 0.989x / 1.336x

The repeated W4A4 control was stable between the two runs: EP8 prologue-run/baseline geomean was 0.999x and TP8 was 1.001x, so the W4A16 difference is not explained by run-to-run drift.

A focused W4A16 route-tile numerical test also passed with the prologue contract (1 passed, 507 deselected), confirming the prototype compiled and produced output within the existing reference tolerance.

Conclusion

The prologue contract is neutral only at the smallest one- or two-token points. Across the complete sweep it is 23.6% slower for EP8 and 16.8% slower for TP8 by geomean, with the largest regressions in the 32-512 token region. Moving the scale into the transform path applies FP32 multiply-and-round work to every decoded weight element, while the current design applies one FP32 scale to each already-accumulated output element. This increases pressure on the online-dequantization pipeline, which is already the sensitive part of W4A16.

Based on this ablation, we should retain the current FP32-accumulator epilogue scaling contract.

@ziang-and
ziang-and force-pushed the agent-cutedsl-moe-bf16-activation branch from 417b07b to 60d18d2 Compare July 23, 2026 23:40
@zianglih

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@zianglih
zianglih marked this pull request as ready for review July 24, 2026 09:18
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@aleozlx
aleozlx merged commit c9172e6 into flashinfer-ai:main Aug 7, 2026
32 of 43 checks passed
aleozlx added a commit to aleozlx/flashinfer that referenced this pull request Aug 7, 2026
Resolves conflicts with flashinfer-ai#4048 (c9172e6), which added
`use_fused_finalize: bool = True` to ExecutionConfig -- the same knob this
branch relocates -- and with the CuteDslNvfp4Runner rework that added the
NVFP4/W4A16 variant split and an enable_pdl hoist.

Resolution:
  * ExecutionConfig keeps only enable_pdl / tune_max_num_tokens; both
    finalize knobs now live on MoEFinalizeConfig, which is the point of
    this branch and of issue flashinfer-ai#4325.
  * MoEFinalizeConfig.use_fused_finalize adopts main's semantics exactly:
    `bool = True` rather than this branch's original `Optional[bool] = None`
    sentinel. main now threads the value explicitly into both inner
    runners, so the "None means backend default" indirection bought
    nothing; a plain bool keeps behaviour identical to main.
  * Both call sites in CuteDslNvfp4Runner (NVFP4 and W4A16 branches) read
    config.finalize.use_fused_finalize; main's enable_pdl hoist and variant
    split are kept as-is.

Verified: repr round-trip for both configs, ExecutionConfig no longer
exposes either finalize knob, use_fused_finalize still defaults True.
pre-commit passes. No GPU available here, so no MoE kernel test was run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aleozlx added a commit that referenced this pull request Aug 7, 2026
## 📌 Description

The rewrite in #4027 changed `tma_load_2d` in
`csrc/fused_moe/monomoe/src/ptx_utils.h` from `.shared::cluster` to
`.shared::cta`. The `.shared::cta` form requires PTX ISA 8.6 / CUDA
12.8+, breaking the `aot-build-import (cu126)` CI job with `ptxas: State
space incorrect for instruction 'cp.async.bulk.tensor'`.

Fix: revert to `.shared::cluster` and remove the erroneous `.tile`
qualifier (`.tile` is only valid with `.shared::cta` and was never
correct on the cluster form). This matches the SM90 fallback in CUTLASS
`cute/arch/copy_sm90_tma.hpp`, which uses `shared::cluster` without
`.tile` for all pre-SM120 targets. Restores CUDA 12.0+ compatibility.

No performance impact: monomoe launches with cluster size 1, so
`.shared::cluster` and `.shared::cta` are semantically identical for
this kernel. The `.tile` qualifier is a PTX syntax disambiguator only;
hardware behavior is unchanged.

The first commit added a CUDA >= 12.8 version gate as an initial
workaround; the second commit replaces it with this root-cause fix and
reverts the gate.

## 🔍 Related Issues

Reported in #4048 (comment): `ptxas monomoe_binding.ptx: State space
incorrect for instruction 'cp.async.bulk.tensor'` on cu126 AOT build.

## 🚀 Pull Request Checklist

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

## 🧪 Tests

- [ ] `aot-build-import (x64, cu126)` and `aot-build-import (arm64,
cu126)` pass in CI
- [ ] `aot-build-import (x64/arm64, cu128/cu129/cu130)` continue to pass

## Reviewer Notes

The PTX change is a one-liner: `shared::cta.global.tile` →
`shared::cluster.global` (dropping `.tile`). CUTLASS's
`cute/arch/copy_sm90_tma.hpp` uses exactly this form for the SM90 path.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved compatibility for tensor loading across supported PTX and
CUDA versions.
  * Updated tensor addressing behavior for single-CTA kernel launches.
  * Improved support across compatible hardware and toolchains.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
IwakuraRein pushed a commit that referenced this pull request Aug 16, 2026
## 📌 Description

@HumansAnd

- Extend CuTe DSL W4A16 fused MoE with tanh-approximate GeGLU and SiTU.
- Reuse the activation math and validation introduced for W4A4 in #4009:
  - GeGLU-tanh: `up * GELU(gate, approximate="tanh")`.
- SiTU: `ActivationType.Swiglu` plus `situ_beta`, with optional
`situ_linear_beta` for the up-branch clamp.
- Apply each expert's W1 alpha before activation, matching the existing
W4A4 contract.
- Include both SiTU parameters in autotuner and compiled-kernel cache
identities.
- Keep activation in GEMM1; GEMM2 routing-weight reduction and fused
finalize are unchanged.
- Extend existing Cartesian tests across W4A4 per-tensor, W4A4
per-token, and W4A16 modes; no new standalone test functions are added.

## 🔍 Related Issues

- #4009
- #4048

## 🚀 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

- [x] I have installed `pre-commit` by running `pip install pre-commit`
(or used your preferred method).
- [x] I have installed the hooks with `pre-commit install`.
- [x] 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](https://pre-commit.com/).

## 🧪 Tests

- [x] Tests have been added or updated as needed.
- [x] All tests are passing (`unittest`, etc.).

B200 source-only validation with
`lmsysorg/sglang:nightly-dev-cu13-20260806-ae5f8c94`:

- `pre-commit run --all-files` — passed.
- GeGLU/SiTU functional and cache-key matrix — 11 passed.
- W4A16 GeGLU/SiTU wrapper-autotune rows — 2 passed, 6 deselected.
- `moe_utils is_aot: False`; editable-install data links resolved inside
the synced source checkout.

## Reviewer Notes

- SiTU intentionally remains `ActivationType.Swiglu` plus its beta
parameters, matching #4009.
- Scope follows #4009's functional and wrapper APIs; the unified
`CuteDslNvfp4Runner` activation configuration is unchanged.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added support for SiLU-in-the-unit (SiTU) parameters in W4A16
mixture-of-experts execution.
  * Added GeGLU-Tanh activation support for applicable gated operations.
* Improved tuning and cache handling for activation and quantization
configurations.

* **Bug Fixes**
* Corrected activation parameter handling across wrapper and functional
execution paths.

* **Tests**
* Expanded accuracy and autotuning coverage across W4A4 and W4A16 modes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
IwakuraRein pushed a commit that referenced this pull request Aug 18, 2026
## 📌 Description

@HumansAnd

Fixes W4A16 CuTe DSL MoE scratch-workspace retention exposed by the
companion SGLang Nemotron-3 integration.

- **Root cause:** each `CuteDslFusedMoEW4A16Runner` retained its sort,
permuted-hidden, and intermediate BF16 tensors in `_workspace_cache`.
The key includes the route tile, so an autotuned runner could retain six
large arenas; model serving then retained a selected arena through every
layer-owned wrapper. These are live tensor references, so
`torch.cuda.empty_cache()` cannot release them.
- **Observed geometry:** at `M=16384`, `top_k=22`, 128 local experts,
hidden 1024, intermediate 2688, and route tile 128, one runner retains
735.75 MiB of hidden scratch plus 1,931.34 MiB of intermediate scratch
and sort buffers. The reproduced 1.89 GiB OOM is the intermediate
allocation.
- **Fix:** make W4A16 scratch invocation-local, matching the established
W4A4 wrapper path. PyTorch's caching allocator reuses eager allocations
after the current-stream work completes, while CUDA graph-private pools
own captured storage and replay addresses.
- **API scope:** no shared/module-global arena, no wrapper sharing, and
no new stream or graph serialization contract. The top-level wrapper,
kernel inputs, tactics, outputs, and numerical contract are unchanged;
the lower-level runner-only `launch_w4a16_moe(..., workspace_cache=...)`
keyword is removed.
- **Validation history:** two temporary behavioral tests at
[`678867c3`](zianglih@678867c)
verified eager workspace lifetime and two-call CUDA-graph replay.
[`c5ea45e4`](zianglih@c5ea45e)
removes them from the final diff for a smaller PR while preserving them
in branch history.

- **Consumer integration:**
[sgl-project/sglang#35120](sgl-project/sglang#35120)
depends on this fix and validates online-quantized Nemotron W4A16
accuracy plus serialized Qwen3 W4A16 disk-reload invariance.

## 🔍 Related Issues

- W4A16 support was introduced in
[#4048](#4048); Nemotron
SiTU support was added in
[#4394](#4394).
- Required by
[sgl-project/sglang#35120](sgl-project/sglang#35120).

## Validation

Environment:

- Image: `lmsysorg/sglang:nightly-dev-cu13-20260817-d91c3682`
- Image digest:
`sha256:fa8774dd128600a09fd6d46670b06fb69a55dac8a3881e50ccf0916a45eb39af`
- Hardware: 8x NVIDIA B300 (SM103), driver `590.48.01`
- PyTorch: `2.13.0+cu130`; CUDA: `13.0`; NCCL: `2.29.7`
- FlashInfer base: `nightly-v0.6.18-20260817` /
`e77a4a0d276367895c3b50a642fd8f326c03fb72`
- FlashInfer validation commit:
`678867c392be6d76f8004e8533c379eb0c222e0f`; final head:
`c5ea45e4422e4c8be8059d79b27581971891132a`
- CuTe DSL: `nvidia-cutlass-dsl==4.7.0`; Nsight Systems: `2026.4.1`

### Workspace lifetime and CUDA graph validation

The following ran at implementation commit `678867c3`; the final head
changes only by removing these temporary tests.

```bash
CUDA_VISIBLE_DEVICES=0 \
PYTHONPATH=/hai-workspace/flashinfer-runtime-w4a16-fix:/hai-workspace/flashinfer-w4a16-workspace \
python3 -m pytest -vv -s \
  /hai-workspace/flashinfer-w4a16-workspace/tests/moe/test_cute_dsl_fused_moe.py::TestCuteDslMoeW4A16::test_wrapper_releases_eager_workspace_with_live_wrappers \
  /hai-workspace/flashinfer-w4a16-workspace/tests/moe/test_cute_dsl_fused_moe.py::TestCuteDslMoeW4A16::test_wrapper_cuda_graph_owns_invocation_workspace
```

```text
# Targeted workspace-lifetime and CUDA-graph regression
test_wrapper_releases_eager_workspace_with_live_wrappers PASSED
test_wrapper_cuda_graph_owns_invocation_workspace PASSED
2 passed, 70 warnings in 918.73s

# Full TestCuteDslMoeW4A16 class (14 existing + 2 temporary tests)
16 passed, 598 warnings in 1133.16s
```

All reported warnings are CuTe DSL or dependency deprecation warnings.

### SGLang Nemotron-3 end-to-end

The companion SGLang PR validates online expert-weight quantization with
the default 16,384-token prefill-graph capacity and
`--mem-fraction-static 0.7`.

```text
# Online FP8 checkpoint with online NVFP4 expert-weight quantization
Capture target prefill CUDA graph end. elapsed=205.47 s, mem usage=4.74 GB, avail mem=56.87 GB.
max_total_num_tokens=30259081, chunked_prefill_size=16384, max_prefill_tokens=16384, available_gpu_mem=54.84 GB
Total latency: 127.126 s
Score: 0.975
Output throughput: 631.254 token/s
avg_spec_accept_length=3.1296052121306137
Ran 1 test in 1890.684s
OK
```

The serialized ModelOpt Qwen3 test also completed two same-checkpoint
disk reloads across weight/CUDA-graph release and resume while
preserving deterministic decode text, token IDs, and logprobs:

```text
# Serialized nvidia/Qwen3-30B-A3B-NVFP4 W4A16 disk reload
Ran 1 test in 1205.499s
OK
```

### PR #4048 distributed benchmark compatibility

This compatibility rerun executed at final PR head
`c5ea45e4422e4c8be8059d79b27581971891132a`.
`benchmarks/bench_cute_dsl_moe_distributed.py` is byte-identical between
#4048's merged head and this head
(`sha256:f61bd31757758bc661ddc94551d04c8a762a27612e0fad10c74c1c3240cc87fa`).
The imported runtime copy is also byte-identical to the final head for
both changed source files:

- `flashinfer/fused_moe/cute_dsl/blackwell/moe_w4a16.py`:
`sha256:cd11e0c1c37653b0a58403de04fa31ea20ae75a6ebab1b8330e7091e32d729f7`
- `flashinfer/fused_moe/cute_dsl/tuner.py`:
`sha256:c2dbbad8f2643f1fcd7f0677ec759d150910f6c44418f261cebd5dcba4c2fd39`

Each of the three complete runs uses 10 warmups and 100 cold-L2,
maximum-rank CUDA-event samples per cell. The HAI pod additionally needs
`PET_LOCAL_ADDR=127.0.0.1` because its hostname is not resolvable in
pod-local DNS. `--allreduce-backend auto` selected FlashInfer MNNVL,
confirmed by a separate one-token `--verbose` diagnostic in the same
environment.

```bash
set -euo pipefail
export FLASHINFER_SOURCE=/hai-workspace/flashinfer-w4a16-workspace
export FLASHINFER_RUNTIME=/hai-workspace/flashinfer-runtime-w4a16-fix
export RESULTS_DIR=/hai-workspace/sglang-cutedsl-w4a16-results/pr4048-distributed-c5ea-20260817b
export PYTHONPATH="${FLASHINFER_RUNTIME}:${FLASHINFER_SOURCE}"
export PET_LOCAL_ADDR=127.0.0.1
export FLASHINFER_DISABLE_VERSION_CHECK=1
export FLASHINFER_NVFP4_4OVER6=1
export FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1
export FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE
export FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1
export FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1
mkdir -p "${RESULTS_DIR}"

for run in 1 2 3; do
  torchrun --standalone --nproc-per-node=8 \
    "${FLASHINFER_SOURCE}/benchmarks/bench_cute_dsl_moe_distributed.py" \
    --warmup 10 --iters 100 \
    --use-per-token-activation --no-fused-finalize \
    2>&1 | tee "${RESULTS_DIR}/end-to-end-run-${run}.log"
done
```

All three runs emitted the expected 52 rows: 13 token counts x EP8/TP8 x
W4A4/W4A16.

<details>
<summary>Run 1: all 52 raw DISTRIBUTED_CSV rows</summary>

```text
DISTRIBUTED_CSV,ep,w4a4,1,8,1.168512
DISTRIBUTED_CSV,ep,w4a16,1,8,0.944432
DISTRIBUTED_CSV,ep,w4a4,2,8,1.226576
DISTRIBUTED_CSV,ep,w4a16,2,8,0.957840
DISTRIBUTED_CSV,ep,w4a4,4,8,1.222032
DISTRIBUTED_CSV,ep,w4a16,4,8,0.916528
DISTRIBUTED_CSV,ep,w4a4,8,8,1.184704
DISTRIBUTED_CSV,ep,w4a16,8,8,0.963344
DISTRIBUTED_CSV,ep,w4a4,16,8,1.224800
DISTRIBUTED_CSV,ep,w4a16,16,8,0.951408
DISTRIBUTED_CSV,ep,w4a4,32,8,1.234592
DISTRIBUTED_CSV,ep,w4a16,32,8,0.967936
DISTRIBUTED_CSV,ep,w4a4,64,8,1.239088
DISTRIBUTED_CSV,ep,w4a16,64,8,0.958080
DISTRIBUTED_CSV,ep,w4a4,128,8,1.227296
DISTRIBUTED_CSV,ep,w4a16,128,8,0.943760
DISTRIBUTED_CSV,ep,w4a4,256,8,1.228256
DISTRIBUTED_CSV,ep,w4a16,256,8,0.955056
DISTRIBUTED_CSV,ep,w4a4,512,8,1.220816
DISTRIBUTED_CSV,ep,w4a16,512,8,0.949472
DISTRIBUTED_CSV,ep,w4a4,1024,8,1.217200
DISTRIBUTED_CSV,ep,w4a16,1024,8,0.950672
DISTRIBUTED_CSV,ep,w4a4,2048,8,1.226176
DISTRIBUTED_CSV,ep,w4a16,2048,8,1.002928
DISTRIBUTED_CSV,ep,w4a4,4096,8,1.270112
DISTRIBUTED_CSV,ep,w4a16,4096,8,1.163104
DISTRIBUTED_CSV,tp,w4a4,1,8,1.246640
DISTRIBUTED_CSV,tp,w4a16,1,8,0.929936
DISTRIBUTED_CSV,tp,w4a4,2,8,1.193216
DISTRIBUTED_CSV,tp,w4a16,2,8,0.905024
DISTRIBUTED_CSV,tp,w4a4,4,8,1.191968
DISTRIBUTED_CSV,tp,w4a16,4,8,0.914336
DISTRIBUTED_CSV,tp,w4a4,8,8,1.232912
DISTRIBUTED_CSV,tp,w4a16,8,8,1.005168
DISTRIBUTED_CSV,tp,w4a4,16,8,1.275024
DISTRIBUTED_CSV,tp,w4a16,16,8,0.988672
DISTRIBUTED_CSV,tp,w4a4,32,8,1.302352
DISTRIBUTED_CSV,tp,w4a16,32,8,0.991168
DISTRIBUTED_CSV,tp,w4a4,64,8,1.287776
DISTRIBUTED_CSV,tp,w4a16,64,8,1.007712
DISTRIBUTED_CSV,tp,w4a4,128,8,1.288544
DISTRIBUTED_CSV,tp,w4a16,128,8,0.976032
DISTRIBUTED_CSV,tp,w4a4,256,8,1.294512
DISTRIBUTED_CSV,tp,w4a16,256,8,1.014432
DISTRIBUTED_CSV,tp,w4a4,512,8,1.325776
DISTRIBUTED_CSV,tp,w4a16,512,8,1.021552
DISTRIBUTED_CSV,tp,w4a4,1024,8,1.333536
DISTRIBUTED_CSV,tp,w4a16,1024,8,1.087632
DISTRIBUTED_CSV,tp,w4a4,2048,8,1.386016
DISTRIBUTED_CSV,tp,w4a16,2048,8,1.226896
DISTRIBUTED_CSV,tp,w4a4,4096,8,1.534096
DISTRIBUTED_CSV,tp,w4a16,4096,8,1.496576
```

</details>

<details>
<summary>Run 2: all 52 raw DISTRIBUTED_CSV rows</summary>

```text
DISTRIBUTED_CSV,ep,w4a4,1,8,1.198144
DISTRIBUTED_CSV,ep,w4a16,1,8,0.936384
DISTRIBUTED_CSV,ep,w4a4,2,8,1.208656
DISTRIBUTED_CSV,ep,w4a16,2,8,0.914576
DISTRIBUTED_CSV,ep,w4a4,4,8,1.227664
DISTRIBUTED_CSV,ep,w4a16,4,8,0.951744
DISTRIBUTED_CSV,ep,w4a4,8,8,1.208256
DISTRIBUTED_CSV,ep,w4a16,8,8,1.068048
DISTRIBUTED_CSV,ep,w4a4,16,8,1.231184
DISTRIBUTED_CSV,ep,w4a16,16,8,0.946000
DISTRIBUTED_CSV,ep,w4a4,32,8,1.234944
DISTRIBUTED_CSV,ep,w4a16,32,8,0.953472
DISTRIBUTED_CSV,ep,w4a4,64,8,1.382608
DISTRIBUTED_CSV,ep,w4a16,64,8,0.955488
DISTRIBUTED_CSV,ep,w4a4,128,8,1.217792
DISTRIBUTED_CSV,ep,w4a16,128,8,0.942240
DISTRIBUTED_CSV,ep,w4a4,256,8,1.223472
DISTRIBUTED_CSV,ep,w4a16,256,8,0.939152
DISTRIBUTED_CSV,ep,w4a4,512,8,1.224704
DISTRIBUTED_CSV,ep,w4a16,512,8,0.953024
DISTRIBUTED_CSV,ep,w4a4,1024,8,1.231840
DISTRIBUTED_CSV,ep,w4a16,1024,8,0.955184
DISTRIBUTED_CSV,ep,w4a4,2048,8,1.230240
DISTRIBUTED_CSV,ep,w4a16,2048,8,0.984656
DISTRIBUTED_CSV,ep,w4a4,4096,8,1.246976
DISTRIBUTED_CSV,ep,w4a16,4096,8,1.152400
DISTRIBUTED_CSV,tp,w4a4,1,8,1.205248
DISTRIBUTED_CSV,tp,w4a16,1,8,0.952688
DISTRIBUTED_CSV,tp,w4a4,2,8,1.221104
DISTRIBUTED_CSV,tp,w4a16,2,8,0.927984
DISTRIBUTED_CSV,tp,w4a4,4,8,1.222752
DISTRIBUTED_CSV,tp,w4a16,4,8,0.930240
DISTRIBUTED_CSV,tp,w4a4,8,8,1.264928
DISTRIBUTED_CSV,tp,w4a16,8,8,1.002752
DISTRIBUTED_CSV,tp,w4a4,16,8,1.280784
DISTRIBUTED_CSV,tp,w4a16,16,8,0.994400
DISTRIBUTED_CSV,tp,w4a4,32,8,1.280864
DISTRIBUTED_CSV,tp,w4a16,32,8,1.002256
DISTRIBUTED_CSV,tp,w4a4,64,8,1.291056
DISTRIBUTED_CSV,tp,w4a16,64,8,1.005040
DISTRIBUTED_CSV,tp,w4a4,128,8,1.281040
DISTRIBUTED_CSV,tp,w4a16,128,8,1.001472
DISTRIBUTED_CSV,tp,w4a4,256,8,1.286368
DISTRIBUTED_CSV,tp,w4a16,256,8,0.997664
DISTRIBUTED_CSV,tp,w4a4,512,8,1.297824
DISTRIBUTED_CSV,tp,w4a16,512,8,1.012064
DISTRIBUTED_CSV,tp,w4a4,1024,8,1.319184
DISTRIBUTED_CSV,tp,w4a16,1024,8,1.068624
DISTRIBUTED_CSV,tp,w4a4,2048,8,1.358832
DISTRIBUTED_CSV,tp,w4a16,2048,8,1.234288
DISTRIBUTED_CSV,tp,w4a4,4096,8,1.534160
DISTRIBUTED_CSV,tp,w4a16,4096,8,1.490592
```

</details>

<details>
<summary>Run 3: all 52 raw DISTRIBUTED_CSV rows</summary>

```text
DISTRIBUTED_CSV,ep,w4a4,1,8,1.154336
DISTRIBUTED_CSV,ep,w4a16,1,8,0.908048
DISTRIBUTED_CSV,ep,w4a4,2,8,1.202480
DISTRIBUTED_CSV,ep,w4a16,2,8,0.942320
DISTRIBUTED_CSV,ep,w4a4,4,8,1.217984
DISTRIBUTED_CSV,ep,w4a16,4,8,0.925856
DISTRIBUTED_CSV,ep,w4a4,8,8,1.219968
DISTRIBUTED_CSV,ep,w4a16,8,8,0.925536
DISTRIBUTED_CSV,ep,w4a4,16,8,1.211344
DISTRIBUTED_CSV,ep,w4a16,16,8,0.944256
DISTRIBUTED_CSV,ep,w4a4,32,8,1.227168
DISTRIBUTED_CSV,ep,w4a16,32,8,0.924864
DISTRIBUTED_CSV,ep,w4a4,64,8,1.209744
DISTRIBUTED_CSV,ep,w4a16,64,8,0.942160
DISTRIBUTED_CSV,ep,w4a4,128,8,1.222128
DISTRIBUTED_CSV,ep,w4a16,128,8,0.950096
DISTRIBUTED_CSV,ep,w4a4,256,8,1.228112
DISTRIBUTED_CSV,ep,w4a16,256,8,0.955936
DISTRIBUTED_CSV,ep,w4a4,512,8,1.238400
DISTRIBUTED_CSV,ep,w4a16,512,8,0.946848
DISTRIBUTED_CSV,ep,w4a4,1024,8,1.222480
DISTRIBUTED_CSV,ep,w4a16,1024,8,0.950592
DISTRIBUTED_CSV,ep,w4a4,2048,8,1.247856
DISTRIBUTED_CSV,ep,w4a16,2048,8,0.995744
DISTRIBUTED_CSV,ep,w4a4,4096,8,1.271680
DISTRIBUTED_CSV,ep,w4a16,4096,8,1.173120
DISTRIBUTED_CSV,tp,w4a4,1,8,1.248944
DISTRIBUTED_CSV,tp,w4a16,1,8,0.931824
DISTRIBUTED_CSV,tp,w4a4,2,8,1.218480
DISTRIBUTED_CSV,tp,w4a16,2,8,0.910384
DISTRIBUTED_CSV,tp,w4a4,4,8,1.222864
DISTRIBUTED_CSV,tp,w4a16,4,8,0.919216
DISTRIBUTED_CSV,tp,w4a4,8,8,1.287648
DISTRIBUTED_CSV,tp,w4a16,8,8,0.991392
DISTRIBUTED_CSV,tp,w4a4,16,8,1.270656
DISTRIBUTED_CSV,tp,w4a16,16,8,0.994000
DISTRIBUTED_CSV,tp,w4a4,32,8,1.291312
DISTRIBUTED_CSV,tp,w4a16,32,8,0.996016
DISTRIBUTED_CSV,tp,w4a4,64,8,1.303648
DISTRIBUTED_CSV,tp,w4a16,64,8,0.994432
DISTRIBUTED_CSV,tp,w4a4,128,8,1.294864
DISTRIBUTED_CSV,tp,w4a16,128,8,1.004208
DISTRIBUTED_CSV,tp,w4a4,256,8,1.309296
DISTRIBUTED_CSV,tp,w4a16,256,8,1.022560
DISTRIBUTED_CSV,tp,w4a4,512,8,1.310064
DISTRIBUTED_CSV,tp,w4a16,512,8,1.026016
DISTRIBUTED_CSV,tp,w4a4,1024,8,1.332736
DISTRIBUTED_CSV,tp,w4a16,1024,8,1.082720
DISTRIBUTED_CSV,tp,w4a4,2048,8,1.383648
DISTRIBUTED_CSV,tp,w4a16,2048,8,1.229296
DISTRIBUTED_CSV,tp,w4a4,4096,8,1.546464
DISTRIBUTED_CSV,tp,w4a16,4096,8,1.511744
```

</details>

The table below takes the median across the three complete runs for each
cell. `speedup = median(W4A4 EP8) / median(strategy)`, so values above 1
favor the strategy.

| Tokens | W4A4 EP8 ms | W4A4 TP8 ms | TP speedup | W4A16 EP8 ms | EP
speedup | W4A16 TP8 ms | TP speedup |
|---:|---:|---:|---:|---:|---:|---:|---:|
| 1 | 1.169 | 1.247 | 0.937x | 0.936 | 1.248x | 0.932 | 1.254x |
| 2 | 1.209 | 1.218 | 0.992x | 0.942 | 1.283x | 0.910 | 1.328x |
| 4 | 1.222 | 1.223 | 0.999x | 0.926 | 1.320x | 0.919 | 1.329x |
| 8 | 1.208 | 1.265 | 0.955x | 0.963 | 1.254x | 1.003 | 1.205x |
| 16 | 1.225 | 1.275 | 0.961x | 0.946 | 1.295x | 0.994 | 1.232x |
| 32 | 1.235 | 1.291 | 0.956x | 0.953 | 1.295x | 0.996 | 1.240x |
| 64 | 1.239 | 1.291 | 0.960x | 0.955 | 1.297x | 1.005 | 1.233x |
| 128 | 1.222 | 1.289 | 0.948x | 0.944 | 1.295x | 1.001 | 1.220x |
| 256 | 1.228 | 1.295 | 0.949x | 0.955 | 1.286x | 1.014 | 1.211x |
| 512 | 1.225 | 1.310 | 0.935x | 0.949 | 1.290x | 1.022 | 1.199x |
| 1024 | 1.222 | 1.333 | 0.917x | 0.951 | 1.286x | 1.083 | 1.129x |
| 2048 | 1.230 | 1.384 | 0.889x | 0.996 | 1.235x | 1.229 | 1.001x |
| 4096 | 1.270 | 1.534 | 0.828x | 1.163 | 1.092x | 1.497 | 0.849x |

Geometric-mean speedups versus W4A4 EP8 are `0.940x` for W4A4 TP8,
`1.266x` for W4A16 EP8, and `1.179x` for W4A16 TP8.

Using the same exported environment above, the Nsight Systems driver
launches its own eight-rank workers for all eight cases: two token
counts x EP8/TP8 x W4A4/W4A16.

```bash
python3 "${FLASHINFER_SOURCE}/benchmarks/bench_cute_dsl_moe_distributed.py" \
  --mode profile_nsys --warmup 10 --profile-iters 20 \
  --use-per-token-activation --no-fused-finalize \
  --nsys-output-dir "${RESULTS_DIR}/nsys" \
  2>&1 | tee "${RESULTS_DIR}/nsys.log"
```

<details>
<summary>32-token cases: four raw Nsight Systems stage tables</summary>

```text
Nsight Systems kernel breakdown: EP8 W4A4, 32 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
routing                   |      1 |    0.35% |      0.559 |       160 |    3.491 | deepseek_v3_topk_kernel
dispatch                  |      1 |    0.15% |      0.240 |       160 |    1.502 | moeA2APrepareDispatchKernel
dispatch                  |      2 |   19.77% |     31.380 |       160 |  196.127 | moeA2ADispatchKernel
dispatch                  |      3 |   11.53% |     18.301 |       160 |  114.379 | moeA2ASanitizeExpertIdsKernel
activation prep/quant     |      1 |    0.11% |      0.169 |       160 |    1.059 | vectorized_elementwise_kernel
activation prep/quant     |      2 |    0.47% |      0.750 |       160 |    4.688 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      1 |    0.39% |      0.619 |       160 |    3.871 | routingIndicesClusterKernel
local MoE                 |      2 |    7.29% |     11.573 |       160 |   72.334 | kernel_cutlass_kernel_flashinf_2...
local MoE                 |      3 |    0.12% |      0.197 |       160 |    1.230 | vectorized_elementwise_kernel
local MoE                 |      4 |    0.81% |      1.282 |       160 |    8.013 | kernel_cutlass_kernel_flashinf_3...
local MoE                 |      5 |    3.28% |      5.204 |       160 |   32.524 | kernel_cutlass_kernel_flashinf_4...
local MoE                 |      6 |    0.88% |      1.400 |       160 |    8.750 | moeUnpermuteKernel
combine                   |      1 |    0.27% |      0.429 |       160 |    2.682 | moeA2APrepareCombineKernel
combine                   |      2 |   54.58% |     86.649 |       160 |  541.557 | moeA2ACombineKernel

Nsight Systems kernel breakdown: EP8 W4A16, 32 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
routing                   |      1 |    0.66% |      0.557 |       160 |    3.483 | deepseek_v3_topk_kernel
dispatch                  |      1 |    0.29% |      0.240 |       160 |    1.498 | moeA2APrepareDispatchKernel
dispatch                  |      2 |   21.84% |     18.310 |       160 |  114.439 | moeA2ADispatchKernel
dispatch                  |      3 |    4.86% |      4.071 |       160 |   25.444 | moeA2ASanitizeExpertIdsKernel
local MoE                 |      1 |    0.76% |      0.639 |       160 |    3.995 | routingIndicesClusterKernel
local MoE                 |      2 |    0.78% |      0.656 |       160 |    4.099 | moePermuteKernel
local MoE                 |      3 |   12.99% |     10.896 |       160 |   68.099 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      4 |    7.05% |      5.910 |       160 |   36.940 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      5 |    1.34% |      1.125 |       160 |    7.032 | moeUnpermuteKernel
combine                   |      1 |    0.47% |      0.390 |       160 |    2.440 | moeA2APrepareCombineKernel
combine                   |      2 |   48.96% |     41.055 |       160 |  256.594 | moeA2ACombineKernel

Nsight Systems kernel breakdown: TP8 W4A4, 32 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
all-gather                |      1 |   13.30% |     35.050 |       160 |  219.062 | ncclDevKernel_AllGather_RING_LL
routing                   |      1 |    0.21% |      0.566 |       160 |    3.535 | deepseek_v3_topk_kernel
activation prep/quant     |      1 |    0.06% |      0.153 |       160 |    0.954 | vectorized_elementwise_kernel
activation prep/quant     |      2 |    0.27% |      0.706 |       160 |    4.411 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      1 |    0.23% |      0.600 |       160 |    3.747 | routingIndicesClusterKernel
local MoE                 |      2 |    4.46% |     11.747 |       160 |   73.420 | kernel_cutlass_kernel_flashinf_2...
local MoE                 |      3 |    0.08% |      0.201 |       160 |    1.257 | vectorized_elementwise_kernel
local MoE                 |      4 |    2.18% |      5.746 |       160 |   35.910 | kernel_cutlass_kernel_flashinf_3...
local MoE                 |      5 |    2.35% |      6.188 |       160 |   38.673 | kernel_cutlass_kernel_flashinf_4...
local MoE                 |      6 |    1.09% |      2.865 |       160 |   17.906 | moeUnpermuteKernel
all-reduce                |      1 |   75.78% |    199.723 |       160 | 1248.270 | twoshotAllreduceKernel

Nsight Systems kernel breakdown: TP8 W4A16, 32 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
all-gather                |      1 |   18.23% |     17.083 |       160 |  106.768 | ncclDevKernel_AllGather_RING_LL
routing                   |      1 |    0.61% |      0.568 |       160 |    3.547 | deepseek_v3_topk_kernel
local MoE                 |      1 |    0.63% |      0.591 |       160 |    3.692 | routingIndicesClusterKernel
local MoE                 |      2 |    0.87% |      0.815 |       160 |    5.093 | moePermuteKernel
local MoE                 |      3 |   11.64% |     10.909 |       160 |   68.178 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      4 |    7.32% |      6.863 |       160 |   42.897 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      5 |    2.21% |      2.067 |       160 |   12.920 | moeUnpermuteKernel
all-reduce                |      1 |   58.50% |     54.823 |       160 |  342.642 | twoshotAllreduceKernel
```

</details>

<details>
<summary>4,096-token cases: four raw Nsight Systems stage
tables</summary>

```text

Nsight Systems kernel breakdown: EP8 W4A4, 4096 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
routing                   |      1 |    0.39% |      0.663 |       160 |    4.143 | deepseek_v3_topk_kernel
dispatch                  |      1 |    0.15% |      0.247 |       160 |    1.546 | moeA2APrepareDispatchKernel
dispatch                  |      2 |   18.26% |     30.738 |       160 |  192.110 | moeA2ADispatchKernel
dispatch                  |      3 |    9.38% |     15.786 |       160 |   98.664 | moeA2ASanitizeExpertIdsKernel
activation prep/quant     |      1 |    0.15% |      0.259 |       160 |    1.620 | vectorized_elementwise_kernel
activation prep/quant     |      2 |    2.04% |      3.435 |       160 |   21.470 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      1 |    0.63% |      1.060 |       160 |    6.623 | routingIndicesClusterKernel
local MoE                 |      2 |   10.34% |     17.407 |       160 |  108.791 | kernel_cutlass_kernel_flashinf_2...
local MoE                 |      3 |    0.23% |      0.385 |       160 |    2.405 | vectorized_elementwise_kernel
local MoE                 |      4 |    5.91% |      9.940 |       160 |   62.123 | kernel_cutlass_kernel_flashinf_3...
local MoE                 |      5 |    5.83% |      9.808 |       160 |   61.299 | kernel_cutlass_kernel_flashinf_4...
local MoE                 |      6 |    3.10% |      5.219 |       160 |   32.616 | moeUnpermuteKernel
combine                   |      1 |    0.93% |      1.571 |       160 |    9.817 | moeA2APrepareCombineKernel
combine                   |      2 |   42.66% |     71.811 |       160 |  448.816 | moeA2ACombineKernel

Nsight Systems kernel breakdown: EP8 W4A16, 4096 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
routing                   |      1 |    0.45% |      0.658 |       160 |    4.112 | deepseek_v3_topk_kernel
dispatch                  |      1 |    0.16% |      0.240 |       160 |    1.500 | moeA2APrepareDispatchKernel
dispatch                  |      2 |   13.16% |     19.373 |       160 |  121.083 | moeA2ADispatchKernel
dispatch                  |      3 |    2.70% |      3.982 |       160 |   24.887 | moeA2ASanitizeExpertIdsKernel
local MoE                 |      1 |    0.71% |      1.039 |       160 |    6.494 | routingIndicesClusterKernel
local MoE                 |      2 |    2.20% |      3.241 |       160 |   20.257 | moePermuteKernel
local MoE                 |      3 |   22.48% |     33.100 |       160 |  206.873 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      4 |   14.91% |     21.957 |       160 |  137.229 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      5 |    3.47% |      5.106 |       160 |   31.915 | moeUnpermuteKernel
combine                   |      1 |    1.44% |      2.125 |       160 |   13.279 | moeA2APrepareCombineKernel
combine                   |      2 |   38.32% |     56.418 |       160 |  352.613 | moeA2ACombineKernel

Nsight Systems kernel breakdown: TP8 W4A4, 4096 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
all-gather                |      1 |   20.71% |     49.212 |       160 |  307.574 | ncclDevKernel_AllGather_RING_LL
routing                   |      1 |    0.68% |      1.627 |       160 |   10.170 | deepseek_v3_topk_kernel
activation prep/quant     |      1 |    0.10% |      0.245 |       160 |    1.534 | vectorized_elementwise_kernel
activation prep/quant     |      2 |    2.01% |      4.774 |       160 |   29.837 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      1 |    0.53% |      1.258 |       160 |    7.864 | routingIndicesClusterKernel
local MoE                 |      2 |    8.26% |     19.630 |       160 |  122.688 | kernel_cutlass_kernel_flashinf_2...
local MoE                 |      3 |    0.09% |      0.215 |       160 |    1.345 | vectorized_elementwise_kernel
local MoE                 |      4 |    4.90% |     11.640 |       160 |   72.748 | kernel_cutlass_kernel_flashinf_3...
local MoE                 |      5 |    9.40% |     22.334 |       160 |  139.585 | kernel_cutlass_kernel_flashinf_4...
local MoE                 |      6 |    7.93% |     18.841 |       160 |  117.757 | moeUnpermuteKernel
all-reduce                |      1 |   45.37% |    107.793 |       160 |  673.706 | twoshotAllreduceKernel

Nsight Systems kernel breakdown: TP8 W4A16, 4096 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
all-gather                |      1 |   14.75% |     31.958 |       160 |  199.739 | ncclDevKernel_AllGather_RING_LL
routing                   |      1 |    0.75% |      1.632 |       160 |   10.197 | deepseek_v3_topk_kernel
local MoE                 |      1 |    0.57% |      1.243 |       160 |    7.769 | routingIndicesClusterKernel
local MoE                 |      2 |    9.78% |     21.189 |       160 |  132.433 | moePermuteKernel
local MoE                 |      3 |   16.70% |     36.189 |       160 |  226.178 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      4 |   18.50% |     40.095 |       160 |  250.596 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      5 |    8.52% |     18.467 |       160 |  115.418 | moeUnpermuteKernel
all-reduce                |      1 |   30.42% |     65.919 |       160 |  411.993 | twoshotAllreduceKernel
```

</details>

The raw `total (ms)` columns above aggregate all eight ranks and 20
captured iterations. Dividing each case's sum by `8 * 20 = 160` gives:

| Tokens | Mode | W4A4 staged ms | W4A16 staged ms | W4A4 / W4A16 |
|---:|:---:|---:|---:|---:|
| 32 | EP8 | 0.992 | 0.524 | 1.893x |
| 32 | TP8 | 1.647 | 0.586 | 2.812x |
| 4096 | EP8 | 1.052 | 0.920 | 1.143x |
| 4096 | TP8 | 1.485 | 1.354 | 1.096x |

Limitations:

- This is a current-head compatibility run, not a base-vs-head
performance A/B. It does not by itself prove that invocation-local
allocation has zero latency overhead.
- It ran on B300/SM103 with CUDA 13.0; #4048's published tables used
B200 with different CUDA/runtime revisions, so absolute timings are not
directly comparable.
- Two end-to-end cells had isolated run ranges around 14% (EP8 W4A16 at
8 tokens and EP8 W4A4 at 64 tokens); use the reported three-run medians
and avoid sub-percent conclusions.
- Nsight staged sums are diagnostic kernel-time aggregates, not
wall-clock latency, and do not subtract overlapping kernels.

## 🚀 Pull Request Checklist

### ✅ 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.

## 🧪 Tests

- [ ] Tests have been added or updated as needed.
- [ ] All tests are passing (`unittest`, etc.).

## Reviewer Notes

Please focus on the workspace lifetime and CUDA-graph ownership
contract. This intentionally follows W4A4's invocation-local scratch
pattern instead of adding a process-global or cross-wrapper workspace
cache. The final diff contains no new test code; the validation-only
behavioral tests and their passing results remain reviewable at
`678867c3`.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved W4A16 mixture-of-experts operation reliability by allocating
required working memory for each invocation.
* Improved handling of workloads with varying sizes by removing reliance
on previously cached capacity.
* **Refactor**
* Simplified workspace management while preserving existing operation
behavior.
* Streamlined the W4A16 execution interface for more predictable
invocation behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@zianglih
zianglih deleted the agent-cutedsl-moe-bf16-activation branch August 19, 2026 07:01
jefby pushed a commit to jefby/flashinfer that referenced this pull request Aug 19, 2026
## 📌 Description

@HumansAnd

- Extend CuTe DSL W4A16 fused MoE with tanh-approximate GeGLU and SiTU.
- Reuse the activation math and validation introduced for W4A4 in flashinfer-ai#4009:
  - GeGLU-tanh: `up * GELU(gate, approximate="tanh")`.
- SiTU: `ActivationType.Swiglu` plus `situ_beta`, with optional
`situ_linear_beta` for the up-branch clamp.
- Apply each expert's W1 alpha before activation, matching the existing
W4A4 contract.
- Include both SiTU parameters in autotuner and compiled-kernel cache
identities.
- Keep activation in GEMM1; GEMM2 routing-weight reduction and fused
finalize are unchanged.
- Extend existing Cartesian tests across W4A4 per-tensor, W4A4
per-token, and W4A16 modes; no new standalone test functions are added.

## 🔍 Related Issues

- flashinfer-ai#4009
- flashinfer-ai#4048

## 🚀 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

- [x] I have installed `pre-commit` by running `pip install pre-commit`
(or used your preferred method).
- [x] I have installed the hooks with `pre-commit install`.
- [x] 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](https://pre-commit.com/).

## 🧪 Tests

- [x] Tests have been added or updated as needed.
- [x] All tests are passing (`unittest`, etc.).

B200 source-only validation with
`lmsysorg/sglang:nightly-dev-cu13-20260806-ae5f8c94`:

- `pre-commit run --all-files` — passed.
- GeGLU/SiTU functional and cache-key matrix — 11 passed.
- W4A16 GeGLU/SiTU wrapper-autotune rows — 2 passed, 6 deselected.
- `moe_utils is_aot: False`; editable-install data links resolved inside
the synced source checkout.

## Reviewer Notes

- SiTU intentionally remains `ActivationType.Swiglu` plus its beta
parameters, matching flashinfer-ai#4009.
- Scope follows flashinfer-ai#4009's functional and wrapper APIs; the unified
`CuteDslNvfp4Runner` activation configuration is unchanged.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added support for SiLU-in-the-unit (SiTU) parameters in W4A16
mixture-of-experts execution.
  * Added GeGLU-Tanh activation support for applicable gated operations.
* Improved tuning and cache handling for activation and quantization
configurations.

* **Bug Fixes**
* Corrected activation parameter handling across wrapper and functional
execution paths.

* **Tests**
* Expanded accuracy and autotuning coverage across W4A4 and W4A16 modes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
jefby pushed a commit to jefby/flashinfer that referenced this pull request Aug 19, 2026
## 📌 Description

@HumansAnd

Fixes W4A16 CuTe DSL MoE scratch-workspace retention exposed by the
companion SGLang Nemotron-3 integration.

- **Root cause:** each `CuteDslFusedMoEW4A16Runner` retained its sort,
permuted-hidden, and intermediate BF16 tensors in `_workspace_cache`.
The key includes the route tile, so an autotuned runner could retain six
large arenas; model serving then retained a selected arena through every
layer-owned wrapper. These are live tensor references, so
`torch.cuda.empty_cache()` cannot release them.
- **Observed geometry:** at `M=16384`, `top_k=22`, 128 local experts,
hidden 1024, intermediate 2688, and route tile 128, one runner retains
735.75 MiB of hidden scratch plus 1,931.34 MiB of intermediate scratch
and sort buffers. The reproduced 1.89 GiB OOM is the intermediate
allocation.
- **Fix:** make W4A16 scratch invocation-local, matching the established
W4A4 wrapper path. PyTorch's caching allocator reuses eager allocations
after the current-stream work completes, while CUDA graph-private pools
own captured storage and replay addresses.
- **API scope:** no shared/module-global arena, no wrapper sharing, and
no new stream or graph serialization contract. The top-level wrapper,
kernel inputs, tactics, outputs, and numerical contract are unchanged;
the lower-level runner-only `launch_w4a16_moe(..., workspace_cache=...)`
keyword is removed.
- **Validation history:** two temporary behavioral tests at
[`678867c3`](zianglih@678867c)
verified eager workspace lifetime and two-call CUDA-graph replay.
[`c5ea45e4`](zianglih@c5ea45e)
removes them from the final diff for a smaller PR while preserving them
in branch history.

- **Consumer integration:**
[sgl-project/sglang#35120](sgl-project/sglang#35120)
depends on this fix and validates online-quantized Nemotron W4A16
accuracy plus serialized Qwen3 W4A16 disk-reload invariance.

## 🔍 Related Issues

- W4A16 support was introduced in
[flashinfer-ai#4048](flashinfer-ai#4048); Nemotron
SiTU support was added in
[flashinfer-ai#4394](flashinfer-ai#4394).
- Required by
[sgl-project/sglang#35120](sgl-project/sglang#35120).

## Validation

Environment:

- Image: `lmsysorg/sglang:nightly-dev-cu13-20260817-d91c3682`
- Image digest:
`sha256:fa8774dd128600a09fd6d46670b06fb69a55dac8a3881e50ccf0916a45eb39af`
- Hardware: 8x NVIDIA B300 (SM103), driver `590.48.01`
- PyTorch: `2.13.0+cu130`; CUDA: `13.0`; NCCL: `2.29.7`
- FlashInfer base: `nightly-v0.6.18-20260817` /
`e77a4a0d276367895c3b50a642fd8f326c03fb72`
- FlashInfer validation commit:
`678867c392be6d76f8004e8533c379eb0c222e0f`; final head:
`c5ea45e4422e4c8be8059d79b27581971891132a`
- CuTe DSL: `nvidia-cutlass-dsl==4.7.0`; Nsight Systems: `2026.4.1`

### Workspace lifetime and CUDA graph validation

The following ran at implementation commit `678867c3`; the final head
changes only by removing these temporary tests.

```bash
CUDA_VISIBLE_DEVICES=0 \
PYTHONPATH=/hai-workspace/flashinfer-runtime-w4a16-fix:/hai-workspace/flashinfer-w4a16-workspace \
python3 -m pytest -vv -s \
  /hai-workspace/flashinfer-w4a16-workspace/tests/moe/test_cute_dsl_fused_moe.py::TestCuteDslMoeW4A16::test_wrapper_releases_eager_workspace_with_live_wrappers \
  /hai-workspace/flashinfer-w4a16-workspace/tests/moe/test_cute_dsl_fused_moe.py::TestCuteDslMoeW4A16::test_wrapper_cuda_graph_owns_invocation_workspace
```

```text
# Targeted workspace-lifetime and CUDA-graph regression
test_wrapper_releases_eager_workspace_with_live_wrappers PASSED
test_wrapper_cuda_graph_owns_invocation_workspace PASSED
2 passed, 70 warnings in 918.73s

# Full TestCuteDslMoeW4A16 class (14 existing + 2 temporary tests)
16 passed, 598 warnings in 1133.16s
```

All reported warnings are CuTe DSL or dependency deprecation warnings.

### SGLang Nemotron-3 end-to-end

The companion SGLang PR validates online expert-weight quantization with
the default 16,384-token prefill-graph capacity and
`--mem-fraction-static 0.7`.

```text
# Online FP8 checkpoint with online NVFP4 expert-weight quantization
Capture target prefill CUDA graph end. elapsed=205.47 s, mem usage=4.74 GB, avail mem=56.87 GB.
max_total_num_tokens=30259081, chunked_prefill_size=16384, max_prefill_tokens=16384, available_gpu_mem=54.84 GB
Total latency: 127.126 s
Score: 0.975
Output throughput: 631.254 token/s
avg_spec_accept_length=3.1296052121306137
Ran 1 test in 1890.684s
OK
```

The serialized ModelOpt Qwen3 test also completed two same-checkpoint
disk reloads across weight/CUDA-graph release and resume while
preserving deterministic decode text, token IDs, and logprobs:

```text
# Serialized nvidia/Qwen3-30B-A3B-NVFP4 W4A16 disk reload
Ran 1 test in 1205.499s
OK
```

### PR flashinfer-ai#4048 distributed benchmark compatibility

This compatibility rerun executed at final PR head
`c5ea45e4422e4c8be8059d79b27581971891132a`.
`benchmarks/bench_cute_dsl_moe_distributed.py` is byte-identical between
flashinfer-ai#4048's merged head and this head
(`sha256:f61bd31757758bc661ddc94551d04c8a762a27612e0fad10c74c1c3240cc87fa`).
The imported runtime copy is also byte-identical to the final head for
both changed source files:

- `flashinfer/fused_moe/cute_dsl/blackwell/moe_w4a16.py`:
`sha256:cd11e0c1c37653b0a58403de04fa31ea20ae75a6ebab1b8330e7091e32d729f7`
- `flashinfer/fused_moe/cute_dsl/tuner.py`:
`sha256:c2dbbad8f2643f1fcd7f0677ec759d150910f6c44418f261cebd5dcba4c2fd39`

Each of the three complete runs uses 10 warmups and 100 cold-L2,
maximum-rank CUDA-event samples per cell. The HAI pod additionally needs
`PET_LOCAL_ADDR=127.0.0.1` because its hostname is not resolvable in
pod-local DNS. `--allreduce-backend auto` selected FlashInfer MNNVL,
confirmed by a separate one-token `--verbose` diagnostic in the same
environment.

```bash
set -euo pipefail
export FLASHINFER_SOURCE=/hai-workspace/flashinfer-w4a16-workspace
export FLASHINFER_RUNTIME=/hai-workspace/flashinfer-runtime-w4a16-fix
export RESULTS_DIR=/hai-workspace/sglang-cutedsl-w4a16-results/pr4048-distributed-c5ea-20260817b
export PYTHONPATH="${FLASHINFER_RUNTIME}:${FLASHINFER_SOURCE}"
export PET_LOCAL_ADDR=127.0.0.1
export FLASHINFER_DISABLE_VERSION_CHECK=1
export FLASHINFER_NVFP4_4OVER6=1
export FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1
export FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE
export FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1
export FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1
mkdir -p "${RESULTS_DIR}"

for run in 1 2 3; do
  torchrun --standalone --nproc-per-node=8 \
    "${FLASHINFER_SOURCE}/benchmarks/bench_cute_dsl_moe_distributed.py" \
    --warmup 10 --iters 100 \
    --use-per-token-activation --no-fused-finalize \
    2>&1 | tee "${RESULTS_DIR}/end-to-end-run-${run}.log"
done
```

All three runs emitted the expected 52 rows: 13 token counts x EP8/TP8 x
W4A4/W4A16.

<details>
<summary>Run 1: all 52 raw DISTRIBUTED_CSV rows</summary>

```text
DISTRIBUTED_CSV,ep,w4a4,1,8,1.168512
DISTRIBUTED_CSV,ep,w4a16,1,8,0.944432
DISTRIBUTED_CSV,ep,w4a4,2,8,1.226576
DISTRIBUTED_CSV,ep,w4a16,2,8,0.957840
DISTRIBUTED_CSV,ep,w4a4,4,8,1.222032
DISTRIBUTED_CSV,ep,w4a16,4,8,0.916528
DISTRIBUTED_CSV,ep,w4a4,8,8,1.184704
DISTRIBUTED_CSV,ep,w4a16,8,8,0.963344
DISTRIBUTED_CSV,ep,w4a4,16,8,1.224800
DISTRIBUTED_CSV,ep,w4a16,16,8,0.951408
DISTRIBUTED_CSV,ep,w4a4,32,8,1.234592
DISTRIBUTED_CSV,ep,w4a16,32,8,0.967936
DISTRIBUTED_CSV,ep,w4a4,64,8,1.239088
DISTRIBUTED_CSV,ep,w4a16,64,8,0.958080
DISTRIBUTED_CSV,ep,w4a4,128,8,1.227296
DISTRIBUTED_CSV,ep,w4a16,128,8,0.943760
DISTRIBUTED_CSV,ep,w4a4,256,8,1.228256
DISTRIBUTED_CSV,ep,w4a16,256,8,0.955056
DISTRIBUTED_CSV,ep,w4a4,512,8,1.220816
DISTRIBUTED_CSV,ep,w4a16,512,8,0.949472
DISTRIBUTED_CSV,ep,w4a4,1024,8,1.217200
DISTRIBUTED_CSV,ep,w4a16,1024,8,0.950672
DISTRIBUTED_CSV,ep,w4a4,2048,8,1.226176
DISTRIBUTED_CSV,ep,w4a16,2048,8,1.002928
DISTRIBUTED_CSV,ep,w4a4,4096,8,1.270112
DISTRIBUTED_CSV,ep,w4a16,4096,8,1.163104
DISTRIBUTED_CSV,tp,w4a4,1,8,1.246640
DISTRIBUTED_CSV,tp,w4a16,1,8,0.929936
DISTRIBUTED_CSV,tp,w4a4,2,8,1.193216
DISTRIBUTED_CSV,tp,w4a16,2,8,0.905024
DISTRIBUTED_CSV,tp,w4a4,4,8,1.191968
DISTRIBUTED_CSV,tp,w4a16,4,8,0.914336
DISTRIBUTED_CSV,tp,w4a4,8,8,1.232912
DISTRIBUTED_CSV,tp,w4a16,8,8,1.005168
DISTRIBUTED_CSV,tp,w4a4,16,8,1.275024
DISTRIBUTED_CSV,tp,w4a16,16,8,0.988672
DISTRIBUTED_CSV,tp,w4a4,32,8,1.302352
DISTRIBUTED_CSV,tp,w4a16,32,8,0.991168
DISTRIBUTED_CSV,tp,w4a4,64,8,1.287776
DISTRIBUTED_CSV,tp,w4a16,64,8,1.007712
DISTRIBUTED_CSV,tp,w4a4,128,8,1.288544
DISTRIBUTED_CSV,tp,w4a16,128,8,0.976032
DISTRIBUTED_CSV,tp,w4a4,256,8,1.294512
DISTRIBUTED_CSV,tp,w4a16,256,8,1.014432
DISTRIBUTED_CSV,tp,w4a4,512,8,1.325776
DISTRIBUTED_CSV,tp,w4a16,512,8,1.021552
DISTRIBUTED_CSV,tp,w4a4,1024,8,1.333536
DISTRIBUTED_CSV,tp,w4a16,1024,8,1.087632
DISTRIBUTED_CSV,tp,w4a4,2048,8,1.386016
DISTRIBUTED_CSV,tp,w4a16,2048,8,1.226896
DISTRIBUTED_CSV,tp,w4a4,4096,8,1.534096
DISTRIBUTED_CSV,tp,w4a16,4096,8,1.496576
```

</details>

<details>
<summary>Run 2: all 52 raw DISTRIBUTED_CSV rows</summary>

```text
DISTRIBUTED_CSV,ep,w4a4,1,8,1.198144
DISTRIBUTED_CSV,ep,w4a16,1,8,0.936384
DISTRIBUTED_CSV,ep,w4a4,2,8,1.208656
DISTRIBUTED_CSV,ep,w4a16,2,8,0.914576
DISTRIBUTED_CSV,ep,w4a4,4,8,1.227664
DISTRIBUTED_CSV,ep,w4a16,4,8,0.951744
DISTRIBUTED_CSV,ep,w4a4,8,8,1.208256
DISTRIBUTED_CSV,ep,w4a16,8,8,1.068048
DISTRIBUTED_CSV,ep,w4a4,16,8,1.231184
DISTRIBUTED_CSV,ep,w4a16,16,8,0.946000
DISTRIBUTED_CSV,ep,w4a4,32,8,1.234944
DISTRIBUTED_CSV,ep,w4a16,32,8,0.953472
DISTRIBUTED_CSV,ep,w4a4,64,8,1.382608
DISTRIBUTED_CSV,ep,w4a16,64,8,0.955488
DISTRIBUTED_CSV,ep,w4a4,128,8,1.217792
DISTRIBUTED_CSV,ep,w4a16,128,8,0.942240
DISTRIBUTED_CSV,ep,w4a4,256,8,1.223472
DISTRIBUTED_CSV,ep,w4a16,256,8,0.939152
DISTRIBUTED_CSV,ep,w4a4,512,8,1.224704
DISTRIBUTED_CSV,ep,w4a16,512,8,0.953024
DISTRIBUTED_CSV,ep,w4a4,1024,8,1.231840
DISTRIBUTED_CSV,ep,w4a16,1024,8,0.955184
DISTRIBUTED_CSV,ep,w4a4,2048,8,1.230240
DISTRIBUTED_CSV,ep,w4a16,2048,8,0.984656
DISTRIBUTED_CSV,ep,w4a4,4096,8,1.246976
DISTRIBUTED_CSV,ep,w4a16,4096,8,1.152400
DISTRIBUTED_CSV,tp,w4a4,1,8,1.205248
DISTRIBUTED_CSV,tp,w4a16,1,8,0.952688
DISTRIBUTED_CSV,tp,w4a4,2,8,1.221104
DISTRIBUTED_CSV,tp,w4a16,2,8,0.927984
DISTRIBUTED_CSV,tp,w4a4,4,8,1.222752
DISTRIBUTED_CSV,tp,w4a16,4,8,0.930240
DISTRIBUTED_CSV,tp,w4a4,8,8,1.264928
DISTRIBUTED_CSV,tp,w4a16,8,8,1.002752
DISTRIBUTED_CSV,tp,w4a4,16,8,1.280784
DISTRIBUTED_CSV,tp,w4a16,16,8,0.994400
DISTRIBUTED_CSV,tp,w4a4,32,8,1.280864
DISTRIBUTED_CSV,tp,w4a16,32,8,1.002256
DISTRIBUTED_CSV,tp,w4a4,64,8,1.291056
DISTRIBUTED_CSV,tp,w4a16,64,8,1.005040
DISTRIBUTED_CSV,tp,w4a4,128,8,1.281040
DISTRIBUTED_CSV,tp,w4a16,128,8,1.001472
DISTRIBUTED_CSV,tp,w4a4,256,8,1.286368
DISTRIBUTED_CSV,tp,w4a16,256,8,0.997664
DISTRIBUTED_CSV,tp,w4a4,512,8,1.297824
DISTRIBUTED_CSV,tp,w4a16,512,8,1.012064
DISTRIBUTED_CSV,tp,w4a4,1024,8,1.319184
DISTRIBUTED_CSV,tp,w4a16,1024,8,1.068624
DISTRIBUTED_CSV,tp,w4a4,2048,8,1.358832
DISTRIBUTED_CSV,tp,w4a16,2048,8,1.234288
DISTRIBUTED_CSV,tp,w4a4,4096,8,1.534160
DISTRIBUTED_CSV,tp,w4a16,4096,8,1.490592
```

</details>

<details>
<summary>Run 3: all 52 raw DISTRIBUTED_CSV rows</summary>

```text
DISTRIBUTED_CSV,ep,w4a4,1,8,1.154336
DISTRIBUTED_CSV,ep,w4a16,1,8,0.908048
DISTRIBUTED_CSV,ep,w4a4,2,8,1.202480
DISTRIBUTED_CSV,ep,w4a16,2,8,0.942320
DISTRIBUTED_CSV,ep,w4a4,4,8,1.217984
DISTRIBUTED_CSV,ep,w4a16,4,8,0.925856
DISTRIBUTED_CSV,ep,w4a4,8,8,1.219968
DISTRIBUTED_CSV,ep,w4a16,8,8,0.925536
DISTRIBUTED_CSV,ep,w4a4,16,8,1.211344
DISTRIBUTED_CSV,ep,w4a16,16,8,0.944256
DISTRIBUTED_CSV,ep,w4a4,32,8,1.227168
DISTRIBUTED_CSV,ep,w4a16,32,8,0.924864
DISTRIBUTED_CSV,ep,w4a4,64,8,1.209744
DISTRIBUTED_CSV,ep,w4a16,64,8,0.942160
DISTRIBUTED_CSV,ep,w4a4,128,8,1.222128
DISTRIBUTED_CSV,ep,w4a16,128,8,0.950096
DISTRIBUTED_CSV,ep,w4a4,256,8,1.228112
DISTRIBUTED_CSV,ep,w4a16,256,8,0.955936
DISTRIBUTED_CSV,ep,w4a4,512,8,1.238400
DISTRIBUTED_CSV,ep,w4a16,512,8,0.946848
DISTRIBUTED_CSV,ep,w4a4,1024,8,1.222480
DISTRIBUTED_CSV,ep,w4a16,1024,8,0.950592
DISTRIBUTED_CSV,ep,w4a4,2048,8,1.247856
DISTRIBUTED_CSV,ep,w4a16,2048,8,0.995744
DISTRIBUTED_CSV,ep,w4a4,4096,8,1.271680
DISTRIBUTED_CSV,ep,w4a16,4096,8,1.173120
DISTRIBUTED_CSV,tp,w4a4,1,8,1.248944
DISTRIBUTED_CSV,tp,w4a16,1,8,0.931824
DISTRIBUTED_CSV,tp,w4a4,2,8,1.218480
DISTRIBUTED_CSV,tp,w4a16,2,8,0.910384
DISTRIBUTED_CSV,tp,w4a4,4,8,1.222864
DISTRIBUTED_CSV,tp,w4a16,4,8,0.919216
DISTRIBUTED_CSV,tp,w4a4,8,8,1.287648
DISTRIBUTED_CSV,tp,w4a16,8,8,0.991392
DISTRIBUTED_CSV,tp,w4a4,16,8,1.270656
DISTRIBUTED_CSV,tp,w4a16,16,8,0.994000
DISTRIBUTED_CSV,tp,w4a4,32,8,1.291312
DISTRIBUTED_CSV,tp,w4a16,32,8,0.996016
DISTRIBUTED_CSV,tp,w4a4,64,8,1.303648
DISTRIBUTED_CSV,tp,w4a16,64,8,0.994432
DISTRIBUTED_CSV,tp,w4a4,128,8,1.294864
DISTRIBUTED_CSV,tp,w4a16,128,8,1.004208
DISTRIBUTED_CSV,tp,w4a4,256,8,1.309296
DISTRIBUTED_CSV,tp,w4a16,256,8,1.022560
DISTRIBUTED_CSV,tp,w4a4,512,8,1.310064
DISTRIBUTED_CSV,tp,w4a16,512,8,1.026016
DISTRIBUTED_CSV,tp,w4a4,1024,8,1.332736
DISTRIBUTED_CSV,tp,w4a16,1024,8,1.082720
DISTRIBUTED_CSV,tp,w4a4,2048,8,1.383648
DISTRIBUTED_CSV,tp,w4a16,2048,8,1.229296
DISTRIBUTED_CSV,tp,w4a4,4096,8,1.546464
DISTRIBUTED_CSV,tp,w4a16,4096,8,1.511744
```

</details>

The table below takes the median across the three complete runs for each
cell. `speedup = median(W4A4 EP8) / median(strategy)`, so values above 1
favor the strategy.

| Tokens | W4A4 EP8 ms | W4A4 TP8 ms | TP speedup | W4A16 EP8 ms | EP
speedup | W4A16 TP8 ms | TP speedup |
|---:|---:|---:|---:|---:|---:|---:|---:|
| 1 | 1.169 | 1.247 | 0.937x | 0.936 | 1.248x | 0.932 | 1.254x |
| 2 | 1.209 | 1.218 | 0.992x | 0.942 | 1.283x | 0.910 | 1.328x |
| 4 | 1.222 | 1.223 | 0.999x | 0.926 | 1.320x | 0.919 | 1.329x |
| 8 | 1.208 | 1.265 | 0.955x | 0.963 | 1.254x | 1.003 | 1.205x |
| 16 | 1.225 | 1.275 | 0.961x | 0.946 | 1.295x | 0.994 | 1.232x |
| 32 | 1.235 | 1.291 | 0.956x | 0.953 | 1.295x | 0.996 | 1.240x |
| 64 | 1.239 | 1.291 | 0.960x | 0.955 | 1.297x | 1.005 | 1.233x |
| 128 | 1.222 | 1.289 | 0.948x | 0.944 | 1.295x | 1.001 | 1.220x |
| 256 | 1.228 | 1.295 | 0.949x | 0.955 | 1.286x | 1.014 | 1.211x |
| 512 | 1.225 | 1.310 | 0.935x | 0.949 | 1.290x | 1.022 | 1.199x |
| 1024 | 1.222 | 1.333 | 0.917x | 0.951 | 1.286x | 1.083 | 1.129x |
| 2048 | 1.230 | 1.384 | 0.889x | 0.996 | 1.235x | 1.229 | 1.001x |
| 4096 | 1.270 | 1.534 | 0.828x | 1.163 | 1.092x | 1.497 | 0.849x |

Geometric-mean speedups versus W4A4 EP8 are `0.940x` for W4A4 TP8,
`1.266x` for W4A16 EP8, and `1.179x` for W4A16 TP8.

Using the same exported environment above, the Nsight Systems driver
launches its own eight-rank workers for all eight cases: two token
counts x EP8/TP8 x W4A4/W4A16.

```bash
python3 "${FLASHINFER_SOURCE}/benchmarks/bench_cute_dsl_moe_distributed.py" \
  --mode profile_nsys --warmup 10 --profile-iters 20 \
  --use-per-token-activation --no-fused-finalize \
  --nsys-output-dir "${RESULTS_DIR}/nsys" \
  2>&1 | tee "${RESULTS_DIR}/nsys.log"
```

<details>
<summary>32-token cases: four raw Nsight Systems stage tables</summary>

```text
Nsight Systems kernel breakdown: EP8 W4A4, 32 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
routing                   |      1 |    0.35% |      0.559 |       160 |    3.491 | deepseek_v3_topk_kernel
dispatch                  |      1 |    0.15% |      0.240 |       160 |    1.502 | moeA2APrepareDispatchKernel
dispatch                  |      2 |   19.77% |     31.380 |       160 |  196.127 | moeA2ADispatchKernel
dispatch                  |      3 |   11.53% |     18.301 |       160 |  114.379 | moeA2ASanitizeExpertIdsKernel
activation prep/quant     |      1 |    0.11% |      0.169 |       160 |    1.059 | vectorized_elementwise_kernel
activation prep/quant     |      2 |    0.47% |      0.750 |       160 |    4.688 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      1 |    0.39% |      0.619 |       160 |    3.871 | routingIndicesClusterKernel
local MoE                 |      2 |    7.29% |     11.573 |       160 |   72.334 | kernel_cutlass_kernel_flashinf_2...
local MoE                 |      3 |    0.12% |      0.197 |       160 |    1.230 | vectorized_elementwise_kernel
local MoE                 |      4 |    0.81% |      1.282 |       160 |    8.013 | kernel_cutlass_kernel_flashinf_3...
local MoE                 |      5 |    3.28% |      5.204 |       160 |   32.524 | kernel_cutlass_kernel_flashinf_4...
local MoE                 |      6 |    0.88% |      1.400 |       160 |    8.750 | moeUnpermuteKernel
combine                   |      1 |    0.27% |      0.429 |       160 |    2.682 | moeA2APrepareCombineKernel
combine                   |      2 |   54.58% |     86.649 |       160 |  541.557 | moeA2ACombineKernel

Nsight Systems kernel breakdown: EP8 W4A16, 32 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
routing                   |      1 |    0.66% |      0.557 |       160 |    3.483 | deepseek_v3_topk_kernel
dispatch                  |      1 |    0.29% |      0.240 |       160 |    1.498 | moeA2APrepareDispatchKernel
dispatch                  |      2 |   21.84% |     18.310 |       160 |  114.439 | moeA2ADispatchKernel
dispatch                  |      3 |    4.86% |      4.071 |       160 |   25.444 | moeA2ASanitizeExpertIdsKernel
local MoE                 |      1 |    0.76% |      0.639 |       160 |    3.995 | routingIndicesClusterKernel
local MoE                 |      2 |    0.78% |      0.656 |       160 |    4.099 | moePermuteKernel
local MoE                 |      3 |   12.99% |     10.896 |       160 |   68.099 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      4 |    7.05% |      5.910 |       160 |   36.940 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      5 |    1.34% |      1.125 |       160 |    7.032 | moeUnpermuteKernel
combine                   |      1 |    0.47% |      0.390 |       160 |    2.440 | moeA2APrepareCombineKernel
combine                   |      2 |   48.96% |     41.055 |       160 |  256.594 | moeA2ACombineKernel

Nsight Systems kernel breakdown: TP8 W4A4, 32 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
all-gather                |      1 |   13.30% |     35.050 |       160 |  219.062 | ncclDevKernel_AllGather_RING_LL
routing                   |      1 |    0.21% |      0.566 |       160 |    3.535 | deepseek_v3_topk_kernel
activation prep/quant     |      1 |    0.06% |      0.153 |       160 |    0.954 | vectorized_elementwise_kernel
activation prep/quant     |      2 |    0.27% |      0.706 |       160 |    4.411 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      1 |    0.23% |      0.600 |       160 |    3.747 | routingIndicesClusterKernel
local MoE                 |      2 |    4.46% |     11.747 |       160 |   73.420 | kernel_cutlass_kernel_flashinf_2...
local MoE                 |      3 |    0.08% |      0.201 |       160 |    1.257 | vectorized_elementwise_kernel
local MoE                 |      4 |    2.18% |      5.746 |       160 |   35.910 | kernel_cutlass_kernel_flashinf_3...
local MoE                 |      5 |    2.35% |      6.188 |       160 |   38.673 | kernel_cutlass_kernel_flashinf_4...
local MoE                 |      6 |    1.09% |      2.865 |       160 |   17.906 | moeUnpermuteKernel
all-reduce                |      1 |   75.78% |    199.723 |       160 | 1248.270 | twoshotAllreduceKernel

Nsight Systems kernel breakdown: TP8 W4A16, 32 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
all-gather                |      1 |   18.23% |     17.083 |       160 |  106.768 | ncclDevKernel_AllGather_RING_LL
routing                   |      1 |    0.61% |      0.568 |       160 |    3.547 | deepseek_v3_topk_kernel
local MoE                 |      1 |    0.63% |      0.591 |       160 |    3.692 | routingIndicesClusterKernel
local MoE                 |      2 |    0.87% |      0.815 |       160 |    5.093 | moePermuteKernel
local MoE                 |      3 |   11.64% |     10.909 |       160 |   68.178 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      4 |    7.32% |      6.863 |       160 |   42.897 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      5 |    2.21% |      2.067 |       160 |   12.920 | moeUnpermuteKernel
all-reduce                |      1 |   58.50% |     54.823 |       160 |  342.642 | twoshotAllreduceKernel
```

</details>

<details>
<summary>4,096-token cases: four raw Nsight Systems stage
tables</summary>

```text

Nsight Systems kernel breakdown: EP8 W4A4, 4096 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
routing                   |      1 |    0.39% |      0.663 |       160 |    4.143 | deepseek_v3_topk_kernel
dispatch                  |      1 |    0.15% |      0.247 |       160 |    1.546 | moeA2APrepareDispatchKernel
dispatch                  |      2 |   18.26% |     30.738 |       160 |  192.110 | moeA2ADispatchKernel
dispatch                  |      3 |    9.38% |     15.786 |       160 |   98.664 | moeA2ASanitizeExpertIdsKernel
activation prep/quant     |      1 |    0.15% |      0.259 |       160 |    1.620 | vectorized_elementwise_kernel
activation prep/quant     |      2 |    2.04% |      3.435 |       160 |   21.470 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      1 |    0.63% |      1.060 |       160 |    6.623 | routingIndicesClusterKernel
local MoE                 |      2 |   10.34% |     17.407 |       160 |  108.791 | kernel_cutlass_kernel_flashinf_2...
local MoE                 |      3 |    0.23% |      0.385 |       160 |    2.405 | vectorized_elementwise_kernel
local MoE                 |      4 |    5.91% |      9.940 |       160 |   62.123 | kernel_cutlass_kernel_flashinf_3...
local MoE                 |      5 |    5.83% |      9.808 |       160 |   61.299 | kernel_cutlass_kernel_flashinf_4...
local MoE                 |      6 |    3.10% |      5.219 |       160 |   32.616 | moeUnpermuteKernel
combine                   |      1 |    0.93% |      1.571 |       160 |    9.817 | moeA2APrepareCombineKernel
combine                   |      2 |   42.66% |     71.811 |       160 |  448.816 | moeA2ACombineKernel

Nsight Systems kernel breakdown: EP8 W4A16, 4096 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
routing                   |      1 |    0.45% |      0.658 |       160 |    4.112 | deepseek_v3_topk_kernel
dispatch                  |      1 |    0.16% |      0.240 |       160 |    1.500 | moeA2APrepareDispatchKernel
dispatch                  |      2 |   13.16% |     19.373 |       160 |  121.083 | moeA2ADispatchKernel
dispatch                  |      3 |    2.70% |      3.982 |       160 |   24.887 | moeA2ASanitizeExpertIdsKernel
local MoE                 |      1 |    0.71% |      1.039 |       160 |    6.494 | routingIndicesClusterKernel
local MoE                 |      2 |    2.20% |      3.241 |       160 |   20.257 | moePermuteKernel
local MoE                 |      3 |   22.48% |     33.100 |       160 |  206.873 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      4 |   14.91% |     21.957 |       160 |  137.229 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      5 |    3.47% |      5.106 |       160 |   31.915 | moeUnpermuteKernel
combine                   |      1 |    1.44% |      2.125 |       160 |   13.279 | moeA2APrepareCombineKernel
combine                   |      2 |   38.32% |     56.418 |       160 |  352.613 | moeA2ACombineKernel

Nsight Systems kernel breakdown: TP8 W4A4, 4096 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
all-gather                |      1 |   20.71% |     49.212 |       160 |  307.574 | ncclDevKernel_AllGather_RING_LL
routing                   |      1 |    0.68% |      1.627 |       160 |   10.170 | deepseek_v3_topk_kernel
activation prep/quant     |      1 |    0.10% |      0.245 |       160 |    1.534 | vectorized_elementwise_kernel
activation prep/quant     |      2 |    2.01% |      4.774 |       160 |   29.837 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      1 |    0.53% |      1.258 |       160 |    7.864 | routingIndicesClusterKernel
local MoE                 |      2 |    8.26% |     19.630 |       160 |  122.688 | kernel_cutlass_kernel_flashinf_2...
local MoE                 |      3 |    0.09% |      0.215 |       160 |    1.345 | vectorized_elementwise_kernel
local MoE                 |      4 |    4.90% |     11.640 |       160 |   72.748 | kernel_cutlass_kernel_flashinf_3...
local MoE                 |      5 |    9.40% |     22.334 |       160 |  139.585 | kernel_cutlass_kernel_flashinf_4...
local MoE                 |      6 |    7.93% |     18.841 |       160 |  117.757 | moeUnpermuteKernel
all-reduce                |      1 |   45.37% |    107.793 |       160 |  673.706 | twoshotAllreduceKernel

Nsight Systems kernel breakdown: TP8 W4A16, 4096 global tokens
stage                     | launch | GPU time | total (ms) | instances | avg (us) | kernel
all-gather                |      1 |   14.75% |     31.958 |       160 |  199.739 | ncclDevKernel_AllGather_RING_LL
routing                   |      1 |    0.75% |      1.632 |       160 |   10.197 | deepseek_v3_topk_kernel
local MoE                 |      1 |    0.57% |      1.243 |       160 |    7.769 | routingIndicesClusterKernel
local MoE                 |      2 |    9.78% |     21.189 |       160 |  132.433 | moePermuteKernel
local MoE                 |      3 |   16.70% |     36.189 |       160 |  226.178 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      4 |   18.50% |     40.095 |       160 |  250.596 | kernel_cutlass_kernel_flashinf_1...
local MoE                 |      5 |    8.52% |     18.467 |       160 |  115.418 | moeUnpermuteKernel
all-reduce                |      1 |   30.42% |     65.919 |       160 |  411.993 | twoshotAllreduceKernel
```

</details>

The raw `total (ms)` columns above aggregate all eight ranks and 20
captured iterations. Dividing each case's sum by `8 * 20 = 160` gives:

| Tokens | Mode | W4A4 staged ms | W4A16 staged ms | W4A4 / W4A16 |
|---:|:---:|---:|---:|---:|
| 32 | EP8 | 0.992 | 0.524 | 1.893x |
| 32 | TP8 | 1.647 | 0.586 | 2.812x |
| 4096 | EP8 | 1.052 | 0.920 | 1.143x |
| 4096 | TP8 | 1.485 | 1.354 | 1.096x |

Limitations:

- This is a current-head compatibility run, not a base-vs-head
performance A/B. It does not by itself prove that invocation-local
allocation has zero latency overhead.
- It ran on B300/SM103 with CUDA 13.0; flashinfer-ai#4048's published tables used
B200 with different CUDA/runtime revisions, so absolute timings are not
directly comparable.
- Two end-to-end cells had isolated run ranges around 14% (EP8 W4A16 at
8 tokens and EP8 W4A4 at 64 tokens); use the reported three-run medians
and avoid sub-percent conclusions.
- Nsight staged sums are diagnostic kernel-time aggregates, not
wall-clock latency, and do not subtract overlapping kernels.

## 🚀 Pull Request Checklist

### ✅ 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.

## 🧪 Tests

- [ ] Tests have been added or updated as needed.
- [ ] All tests are passing (`unittest`, etc.).

## Reviewer Notes

Please focus on the workspace lifetime and CUDA-graph ownership
contract. This intentionally follows W4A4's invocation-local scratch
pattern instead of adding a process-global or cross-wrapper workspace
cache. The final diff contains no new test code; the validation-only
behavioral tests and their passing results remain reviewable at
`678867c3`.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved W4A16 mixture-of-experts operation reliability by allocating
required working memory for each invocation.
* Improved handling of workloads with varying sizes by removing reliance
on previously cached capacity.
* **Refactor**
* Simplified workspace management while preserving existing operation
behavior.
* Streamlined the W4A16 execution interface for more predictable
invocation behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
aleozlx pushed a commit that referenced this pull request Sep 10, 2026
<!-- .github/pull_request_template.md -->

## 📌 Description

@humansand

Compare CuTe DSL W4A16/W4A4 MoE against pure TRTLLM BF16 using the same
DeepSeek-V3 source inputs. Extend `benchmarks/bench_moe_deepseek.py`
with BF16 and NVFP4 precision modes in the existing `bench_trtllm`
helper.

- **BF16 contract:** BF16 activations and expert weights; public
`TrtllmBf16Config.prepare_weights` applies gated row reorder and
BlockMajorK layout outside timing, with no quantization.
- **Integration:** `TRTLLM NVFP4` / `TRTLLM BF16` output and
`trtllm-nvfp4` / `trtllm-bf16` selectors for benchmarking/profiling;
shared routing, autotuning and measurement setup with explicit precision
branches. Both precisions reuse the same cached routed realization per
input row.
- **Finalize:** `--no-fused-finalize` applies to CuTe. TRTLLM retains
native finalize; FP4 activation flags do not affect pure BF16.
- **Scope:** benchmark script only; no kernel or distributed-script
changes. Three configurations below: inference per-tensor, inference
per-token, and the deterministic RL configuration from #4048.

### Source and environment

- **Measured source:** `1ac2233d43df747af455ed4a8406d7439d686d9f`
(FlashInfer 0.6.18), used for all nine sweeps. **Implementation head:**
`2079fa00c4a2a0fb2e95bb284978a703653a4830`. The nine sweeps precede the
shared-helper/name/CLI-validation updates. Separate diagnostic
measurements below identify their own source commits; they do not
replace the original sweeps. Paired refactor correctness validation is
linked in Tests.
- **Hardware:** retained C2 devbox, one B300 SXM6 AC
(`CUDA_VISIBLE_DEVICES=0`, SM103, 148 SMs, 267.7 GiB) on an eight-B300
host; driver 590.48.01 and image CUDA compatibility library 595.58.03.
- **Image:** `nvcr.io/nvidia/pytorch:26.05-py3`; digest
`nvcr.io/nvidia/pytorch@sha256:222d8b18e671be5c3ef91cb41727a2572a0b23f59ded6c39f373a96946f6f2ba`.
- **Software:** CUDA toolkit 13.2 (`nvcc V13.2.78`), PyTorch
`2.12.0a0+5aff3928d8.nv26.05`, Python 3.12.3, CuTe DSL `[cu13]==4.7.0`,
`cupti-python==13.2.0`, `nvidia-cuda-cupti==13.2.86`,
`apache-tvm-ffi==0.1.13.post3`; `nvidia-cuda-nvdisasm==13.3.73` is the
DSL disassembler dependency, while compiler/runtime remain CUDA 13.2.
- **Provenance:** editable metadata still reports initial commit
`e713467f`; the measured Git checkout is `1ac2233d`. Their difference
only shares routed benchmark inputs across TRTLLM precisions. Warm
compilation caches are retained.

### Reproduction and configuration

Run inside the image above. Setup the exact measured source; the
optional EP-transport build is disabled because no communication is
performed.

```sh
set -euo pipefail
mkdir -p /workspace/flashinfer-moe-bf16-benchmark/artifacts
cd /workspace/flashinfer-moe-bf16-benchmark
git clone --branch zianglih/bench-deepseek-moe-bf16 https://github.com/zianglih/flashinfer.git flashinfer
cd flashinfer
git checkout --detach 1ac2233d43df747af455ed4a8406d7439d686d9f
git submodule update --init --recursive
export BUILD_NVEP=0 FLASHINFER_BUILD_NO_PIP=1 MAX_JOBS=8
python3 -m pip install 'setuptools>=77' 'apache-tvm-ffi>=0.1.6,!=0.1.8,!=0.1.8.post0,<0.2'
python3 -m pip install 'nvidia-cutlass-dsl[cu13]==4.7.0' 'cupti-python==13.2.0' 'nvidia-cuda-cupti==13.2.86' 'nvidia-cuda-nvdisasm==13.3.73' ninja einops pytest pytest-timeout
python3 -m pip install --no-build-isolation --no-deps -e .
python3 -m flashinfer.collect_env > ../artifacts/environment.txt
python3 -m pip freeze > ../artifacts/pip-freeze.txt
nvcc --version >> ../artifacts/environment.txt
nvidia-smi --query-gpu=name,uuid,driver_version,memory.total --format=csv >> ../artifacts/environment.txt
```

Run each command below from that checkout. All commands use logits
routing, wrapper API, TP1, initial activation quantization included,
CUDA graphs, CUPTI and autotuning. The existing order was per-token 1,
RL 1, per-token 2, RL 2, per-token 3, RL 3. Only the added per-tensor
variant was subsequently run three times consecutively. Every repetition
starts a fresh Python process.

**Inference per-tensor — `Per-token activation: False`:** omit
`--use-per-token-activation`; use the existing global-scale path, 4over6
disabled, FP4 quantization fast math enabled, CuTe fused atomic
finalize. The default CLI includes CUTLASS NVFP4 in this configuration.

```sh
env \
  -u FLASHINFER_NVFP4_4OVER6 \
  -u FLASHINFER_NVFP4_4OVER6_E4M3_USE_256 \
  -u FLASHINFER_NVFP4_4OVER6_ERR_MODE \
  -u FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH \
  -u FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH \
  CUDA_VISIBLE_DEVICES=0 FLASHINFER_DISABLE_VERSION_CHECK=1 MAX_JOBS=8 \
  python3 benchmarks/bench_moe_deepseek.py \
    --num-tokens 1,2,4,8,16,32,64,128,256,512,1024,2048,4096 \
    --ep 8 --warmup 10 --iters 100 --routing-input-mode logits \
    --include-activation-quant
```

**Inference per-token — `Per-token activation: True`:** per-token
activation scaling, 4over6 disabled, FP4 quantization fast math enabled,
CuTe fused atomic finalize. CUTLASS is omitted because it does not
consume per-token scales.

```sh
env \
  -u FLASHINFER_NVFP4_4OVER6 \
  -u FLASHINFER_NVFP4_4OVER6_E4M3_USE_256 \
  -u FLASHINFER_NVFP4_4OVER6_ERR_MODE \
  -u FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH \
  -u FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH \
  CUDA_VISIBLE_DEVICES=0 FLASHINFER_DISABLE_VERSION_CHECK=1 MAX_JOBS=8 \
  python3 benchmarks/bench_moe_deepseek.py \
    --num-tokens 1,2,4,8,16,32,64,128,256,512,1024,2048,4096 \
    --ep 8 --warmup 10 --iters 100 --routing-input-mode logits \
    --include-activation-quant --use-per-token-activation
```

**Deterministic RL — `Per-token activation: True`:** per-token 4over6
MSE, E4M3 max 256, error fast math enabled, FP4 quantization fast math
disabled, CuTe two-stage finalize. This is the requested configuration
name, not a full-model determinism claim; CUTLASS is omitted.

```sh
env \
  -u FLASHINFER_NVFP4_4OVER6 \
  -u FLASHINFER_NVFP4_4OVER6_E4M3_USE_256 \
  -u FLASHINFER_NVFP4_4OVER6_ERR_MODE \
  -u FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH \
  -u FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH \
  CUDA_VISIBLE_DEVICES=0 FLASHINFER_DISABLE_VERSION_CHECK=1 MAX_JOBS=8 \
  FLASHINFER_NVFP4_4OVER6=1 \
  FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1 \
  FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE \
  FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1 \
  FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1 \
  python3 benchmarks/bench_moe_deepseek.py \
    --num-tokens 1,2,4,8,16,32,64,128,256,512,1024,2048,4096 \
    --ep 8 --warmup 10 --iters 100 --routing-input-mode logits \
    --include-activation-quant --use-per-token-activation --no-fused-finalize
```

- **Timing:** 10 warmups, 100 iterations/case, cold L2; each run reports
median iteration latency. Routing, expert computation, finalize and
initial FP4 activation quantization are timed. W4A16/BF16 consume BF16
directly. Weight preparation, compilation and autotuning are outside
measurement.
- **Workload:** H=7168, I=2048, E=256, 32 local experts (EP8
simulation), top-k=8, 8 groups/top-4 groups, routed scale=2.5, bias
scale=0.01, seed 42, tokens 1–4096 in powers of two. All arms share
source BF16 inputs and logits within each row. TFLOPS uses expected
uniform local work fraction 32/256; printed routing statistics show
actual local routes.
- **Capture:** an outer wrapper serializes returned `BenchResult`
records after measurement; original CLI/timing functions are unchanged.
All nine processes exited 0. All 507 captured latencies (6×13×4 +
3×13×5) were checked against the printed values before aggregation.

### Complete raw performance results

All nine complete result tables follow. Backend names and table spacing
are normalized for display; decorative divider lines are omitted.
Original logs and captures remain unchanged, as do every result value
and column. Identical printed configuration headers are shown once per
three-run group. Raw latency prints to 0.001 ms; raw speedups use
unrounded values. The existing six per-token/RL measurements remain
unchanged; only the three per-tensor runs were added.

**Inference per-tensor — printed configuration for all three runs:**

```text
DeepSeek-V3 MoE Performance Benchmark
GPU: NVIDIA B300 SXM6 AC
CuteDSL API: Wrapper
Per-token activation: False
Initial activation quantization: True
CuteDSL modes: W4A4 and W4A16; baselines: TRTLLM NVFP4 and TRTLLM BF16
Tensor parallelism simulation: TP=1
CUDA profiler capture: False
CuteDSL finalize: atomic fused
TRTLLM NVFP4 / TRTLLM BF16 finalize: native (unaffected by --no-fused-finalize).
DeepSeek-V3 MoE Benchmark: CuteDSL W4A4/W4A16 vs CUTLASS vs TRTLLM NVFP4 / TRTLLM BF16 (EP=8, TP=1)
Model: hidden=7168, intermediate=2048, experts=256, top_k=8
EP Config: 32 local experts (simulating 8-way parallelism)
TP Config: intermediate size 2048 (simulating 1-way parallelism)
CUDA Graph: enabled, CUPTI: enabled
Routing bias scale: 0.01 (larger values tend to create expert imbalance)
Timed initial activation quantization for FP4-activation backends: included; W4A16 and TRTLLM BF16 consume BF16 directly
CuteDSL finalize: atomic fused
TRTLLM NVFP4 / TRTLLM BF16 finalize: native (unaffected by --no-fused-finalize).
```

<details>
<summary>Inference per-tensor run 1 — 13 token counts, 5
backends</summary>

```text
Tokens | CuteDSL W4A4  | CuteDSL W4A16 | CUTLASS       | TRTLLM NVFP4  | TRTLLM BF16   | Speedup vs CUTLASS | Speedup vs TRTLLM NVFP4 | Speedup vs TRTLLM BF16 | Winner       | Active  | Stats
       | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | W4A4     W4A16     | W4A4     W4A16          | W4A4     W4A16         |              | experts | min/max/median
1      | 0.031     2.8 | 0.040     2.2 | 0.048     1.8 | 0.026     3.4 | 0.051     1.7 | 1.53x    1.21x     | 0.82x    0.65x          | 1.63x    1.29x         | TRTLLM NVFP4 | 2       | 0/  1/   0.00
2      | 0.040     4.4 | 0.046     3.9 | 0.058     3.1 | 0.036     4.9 | 0.079     2.2 | 1.43x    1.26x     | 0.89x    0.79x          | 1.95x    1.72x         | TRTLLM NVFP4 | 4       | 0/  1/   0.00
4      | 0.057     6.2 | 0.063     5.6 | 0.070     5.0 | 0.051     6.9 | 0.117     3.0 | 1.24x    1.12x     | 0.89x    0.81x          | 2.05x    1.85x         | TRTLLM NVFP4 | 7       | 0/  1/   0.00
8      | 0.073     9.7 | 0.079     8.9 | 0.086     8.2 | 0.064    11.0 | 0.157     4.5 | 1.18x    1.10x     | 0.88x    0.81x          | 2.15x    1.99x         | TRTLLM NVFP4 | 10      | 0/  2/   0.00
16     | 0.083    17.0 | 0.085    16.5 | 0.097    14.5 | 0.076    18.7 | 0.192     7.3 | 1.17x    1.14x     | 0.91x    0.89x          | 2.32x    2.25x         | TRTLLM NVFP4 | 13      | 0/  2/   0.00
32     | 0.113    24.8 | 0.119    23.8 | 0.127    22.1 | 0.106    26.5 | 0.285     9.9 | 1.12x    1.07x     | 0.94x    0.90x          | 2.52x    2.41x         | TRTLLM NVFP4 | 21      | 0/  3/   1.00
64     | 0.146    38.5 | 0.153    36.9 | 0.159    35.6 | 0.139    40.6 | 0.388    14.5 | 1.08x    1.04x     | 0.95x    0.91x          | 2.65x    2.54x         | TRTLLM NVFP4 | 29      | 0/  5/   2.00
128    | 0.150    75.3 | 0.158    71.5 | 0.164    68.8 | 0.144    78.3 | 0.399    28.3 | 1.10x    1.04x     | 0.96x    0.91x          | 2.66x    2.53x         | TRTLLM NVFP4 | 30      | 0/  8/   3.00
256    | 0.158   142.8 | 0.164   137.2 | 0.173   130.0 | 0.154   146.7 | 0.427    52.8 | 1.10x    1.06x     | 0.97x    0.94x          | 2.71x    2.60x         | TRTLLM NVFP4 | 32      | 1/ 11/   7.00
512    | 0.160   282.2 | 0.169   267.2 | 0.179   251.4 | 0.164   275.3 | 0.435   103.7 | 1.12x    1.06x     | 1.03x    0.97x          | 2.72x    2.58x         | W4A4         | 32      | 7/ 23/  14.00
1024   | 0.165   545.5 | 0.189   476.0 | 0.197   458.2 | 0.295   305.4 | 0.444   203.3 | 1.19x    1.04x     | 1.79x    1.56x          | 2.68x    2.34x         | W4A4         | 32      | 17/ 42/  28.50
2048   | 0.177  1018.3 | 0.233   773.7 | 0.223   810.3 | 0.299   602.6 | 0.475   379.4 | 1.26x    0.95x     | 1.69x    1.28x          | 2.68x    2.04x         | W4A4         | 32      | 38/ 74/  57.50
4096   | 0.219  1646.7 | 0.363   993.0 | 0.281  1282.8 | 0.309  1167.4 | 0.529   682.5 | 1.28x    0.77x     | 1.41x    0.85x          | 2.41x    1.45x         | W4A4         | 32      | 81/147/ 117.00
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend
```

</details>

<details>
<summary>Inference per-tensor run 2 — 13 token counts, 5
backends</summary>

```text
Tokens | CuteDSL W4A4  | CuteDSL W4A16 | CUTLASS       | TRTLLM NVFP4  | TRTLLM BF16   | Speedup vs CUTLASS | Speedup vs TRTLLM NVFP4 | Speedup vs TRTLLM BF16 | Winner       | Active  | Stats
       | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | W4A4     W4A16     | W4A4     W4A16          | W4A4     W4A16         |              | experts | min/max/median
1      | 0.031     2.8 | 0.039     2.2 | 0.048     1.8 | 0.026     3.4 | 0.051     1.7 | 1.53x    1.21x     | 0.82x    0.65x          | 1.63x    1.29x         | TRTLLM NVFP4 | 2       | 0/  1/   0.00
2      | 0.040     4.4 | 0.046     3.8 | 0.058     3.1 | 0.036     4.9 | 0.079     2.2 | 1.43x    1.26x     | 0.89x    0.79x          | 1.95x    1.72x         | TRTLLM NVFP4 | 4       | 0/  1/   0.00
4      | 0.057     6.2 | 0.063     5.6 | 0.070     5.0 | 0.051     6.9 | 0.117     3.0 | 1.24x    1.12x     | 0.89x    0.81x          | 2.05x    1.85x         | TRTLLM NVFP4 | 7       | 0/  1/   0.00
8      | 0.073     9.7 | 0.079     9.0 | 0.086     8.2 | 0.064    11.0 | 0.157     4.5 | 1.18x    1.10x     | 0.88x    0.81x          | 2.15x    1.99x         | TRTLLM NVFP4 | 10      | 0/  2/   0.00
16     | 0.083    17.0 | 0.088    16.0 | 0.097    14.5 | 0.076    18.6 | 0.192     7.3 | 1.17x    1.10x     | 0.91x    0.86x          | 2.32x    2.17x         | TRTLLM NVFP4 | 13      | 0/  2/   0.00
32     | 0.114    24.8 | 0.118    23.8 | 0.127    22.1 | 0.106    26.6 | 0.285     9.9 | 1.12x    1.08x     | 0.93x    0.90x          | 2.51x    2.41x         | TRTLLM NVFP4 | 21      | 0/  3/   1.00
64     | 0.147    38.4 | 0.151    37.4 | 0.159    35.5 | 0.140    40.3 | 0.388    14.5 | 1.08x    1.05x     | 0.95x    0.93x          | 2.64x    2.57x         | TRTLLM NVFP4 | 29      | 0/  5/   2.00
128    | 0.150    75.3 | 0.158    71.3 | 0.164    68.8 | 0.144    78.2 | 0.399    28.2 | 1.09x    1.04x     | 0.96x    0.91x          | 2.67x    2.53x         | TRTLLM NVFP4 | 30      | 0/  8/   3.00
256    | 0.158   143.0 | 0.164   137.1 | 0.173   130.2 | 0.154   146.5 | 0.428    52.7 | 1.10x    1.05x     | 0.98x    0.94x          | 2.71x    2.60x         | TRTLLM NVFP4 | 32      | 1/ 11/   7.00
512    | 0.160   281.9 | 0.169   267.3 | 0.179   251.3 | 0.164   275.4 | 0.435   103.8 | 1.12x    1.06x     | 1.02x    0.97x          | 2.72x    2.58x         | W4A4         | 32      | 7/ 23/  14.00
1024   | 0.165   546.4 | 0.190   475.7 | 0.197   458.4 | 0.295   305.4 | 0.443   203.6 | 1.19x    1.04x     | 1.79x    1.56x          | 2.68x    2.34x         | W4A4         | 32      | 17/ 42/  28.50
2048   | 0.177  1018.2 | 0.233   772.9 | 0.223   810.6 | 0.299   603.6 | 0.476   379.1 | 1.26x    0.95x     | 1.69x    1.28x          | 2.69x    2.04x         | W4A4         | 32      | 38/ 74/  57.50
4096   | 0.217  1661.9 | 0.363   994.3 | 0.281  1283.9 | 0.309  1166.6 | 0.530   680.7 | 1.29x    0.77x     | 1.42x    0.85x          | 2.44x    1.46x         | W4A4         | 32      | 81/147/ 117.00
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend
```

</details>

<details>
<summary>Inference per-tensor run 3 — 13 token counts, 5
backends</summary>

```text
Tokens | CuteDSL W4A4  | CuteDSL W4A16 | CUTLASS       | TRTLLM NVFP4  | TRTLLM BF16   | Speedup vs CUTLASS | Speedup vs TRTLLM NVFP4 | Speedup vs TRTLLM BF16 | Winner       | Active  | Stats
       | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | W4A4     W4A16     | W4A4     W4A16          | W4A4     W4A16         |              | experts | min/max/median
1      | 0.031     2.8 | 0.040     2.2 | 0.048     1.8 | 0.026     3.4 | 0.051     1.7 | 1.53x    1.21x     | 0.82x    0.65x          | 1.64x    1.29x         | TRTLLM NVFP4 | 2       | 0/  1/   0.00
2      | 0.043     4.1 | 0.046     3.9 | 0.058     3.1 | 0.036     4.9 | 0.078     2.2 | 1.34x    1.26x     | 0.83x    0.79x          | 1.82x    1.71x         | TRTLLM NVFP4 | 4       | 0/  1/   0.00
4      | 0.057     6.2 | 0.063     5.6 | 0.071     5.0 | 0.051     6.9 | 0.117     3.0 | 1.24x    1.12x     | 0.90x    0.81x          | 2.05x    1.85x         | TRTLLM NVFP4 | 7       | 0/  1/   0.00
8      | 0.073     9.7 | 0.079     9.0 | 0.086     8.2 | 0.064    11.0 | 0.156     4.5 | 1.18x    1.10x     | 0.88x    0.81x          | 2.15x    1.99x         | TRTLLM NVFP4 | 10      | 0/  2/   0.00
16     | 0.083    17.0 | 0.085    16.5 | 0.097    14.5 | 0.076    18.6 | 0.189     7.5 | 1.17x    1.14x     | 0.91x    0.89x          | 2.28x    2.22x         | TRTLLM NVFP4 | 13      | 0/  2/   0.00
32     | 0.114    24.8 | 0.118    23.8 | 0.127    22.1 | 0.106    26.6 | 0.286     9.9 | 1.12x    1.08x     | 0.93x    0.89x          | 2.52x    2.41x         | TRTLLM NVFP4 | 21      | 0/  3/   1.00
64     | 0.147    38.5 | 0.151    37.3 | 0.158    35.6 | 0.140    40.3 | 0.387    14.6 | 1.08x    1.05x     | 0.95x    0.93x          | 2.64x    2.56x         | TRTLLM NVFP4 | 29      | 0/  5/   2.00
128    | 0.150    75.4 | 0.156    72.1 | 0.164    68.9 | 0.144    78.2 | 0.399    28.3 | 1.09x    1.05x     | 0.96x    0.92x          | 2.67x    2.55x         | TRTLLM NVFP4 | 30      | 0/  8/   3.00
256    | 0.158   142.7 | 0.165   136.7 | 0.173   130.0 | 0.154   146.9 | 0.428    52.7 | 1.10x    1.05x     | 0.97x    0.93x          | 2.70x    2.59x         | TRTLLM NVFP4 | 32      | 1/ 11/   7.00
512    | 0.160   282.0 | 0.169   267.2 | 0.179   251.4 | 0.164   275.1 | 0.435   103.7 | 1.12x    1.06x     | 1.03x    0.97x          | 2.72x    2.58x         | W4A4         | 32      | 7/ 23/  14.00
1024   | 0.165   546.2 | 0.190   475.9 | 0.195   463.0 | 0.296   304.9 | 0.443   203.5 | 1.18x    1.03x     | 1.79x    1.56x          | 2.68x    2.34x         | W4A4         | 32      | 17/ 42/  28.50
2048   | 0.177  1019.1 | 0.233   774.0 | 0.223   810.6 | 0.299   603.0 | 0.477   378.4 | 1.26x    0.95x     | 1.69x    1.28x          | 2.69x    2.05x         | W4A4         | 32      | 38/ 74/  57.50
4096   | 0.217  1661.4 | 0.363   993.5 | 0.280  1288.5 | 0.309  1168.4 | 0.529   681.8 | 1.29x    0.77x     | 1.42x    0.85x          | 2.44x    1.46x         | W4A4         | 32      | 81/147/ 117.00
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend
```

</details>

**Inference per-token — printed configuration for all three runs:**

```text
DeepSeek-V3 MoE Performance Benchmark
GPU: NVIDIA B300 SXM6 AC
CuteDSL API: Wrapper
Per-token activation: True
Initial activation quantization: True
CuteDSL modes: W4A4 and W4A16; baselines: TRTLLM NVFP4 and TRTLLM BF16
Tensor parallelism simulation: TP=1
CUDA profiler capture: False
CuteDSL finalize: atomic fused
TRTLLM NVFP4 / TRTLLM BF16 finalize: native (unaffected by --no-fused-finalize).
DeepSeek-V3 MoE Benchmark: CuteDSL W4A4/W4A16 vs TRTLLM NVFP4 / TRTLLM BF16 (EP=8, TP=1)
Model: hidden=7168, intermediate=2048, experts=256, top_k=8
EP Config: 32 local experts (simulating 8-way parallelism)
TP Config: intermediate size 2048 (simulating 1-way parallelism)
CUDA Graph: enabled, CUPTI: enabled
Routing bias scale: 0.01 (larger values tend to create expert imbalance)
Timed initial activation quantization for FP4-activation backends: included; W4A16 and TRTLLM BF16 consume BF16 directly
CuteDSL finalize: atomic fused
TRTLLM NVFP4 / TRTLLM BF16 finalize: native (unaffected by --no-fused-finalize).
CUTLASS omitted: it does not consume the per-token activation scale.
```

<details>
<summary>Inference per-token run 1 — 13 token counts, 4
backends</summary>

```text
Tokens | CuteDSL W4A4  | CuteDSL W4A16 | TRTLLM NVFP4  | TRTLLM BF16   | Speedup vs TRTLLM NVFP4 | Speedup vs TRTLLM BF16 | Winner       | Active  | Stats
       | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | W4A4     W4A16          | W4A4     W4A16         |              | experts | min/max/median
1      | 0.042     2.1 | 0.040     2.2 | 0.034     2.6 | 0.053     1.7 | 0.82x    0.87x          | 1.26x    1.34x         | TRTLLM NVFP4 | 2       | 0/  1/   0.00
2      | 0.053     3.3 | 0.046     3.9 | 0.046     3.8 | 0.079     2.2 | 0.87x    1.01x          | 1.49x    1.72x         | W4A16        | 4       | 0/  1/   0.00
4      | 0.072     4.9 | 0.062     5.7 | 0.060     5.9 | 0.116     3.0 | 0.83x    0.97x          | 1.61x    1.87x         | TRTLLM NVFP4 | 7       | 0/  1/   0.00
8      | 0.088     8.0 | 0.078     9.0 | 0.074     9.5 | 0.157     4.5 | 0.84x    0.95x          | 1.79x    2.01x         | TRTLLM NVFP4 | 10      | 0/  2/   0.00
16     | 0.097    14.6 | 0.084    16.8 | 0.086    16.3 | 0.190     7.4 | 0.89x    1.03x          | 1.97x    2.27x         | W4A16        | 13      | 0/  2/   0.00
32     | 0.132    21.4 | 0.120    23.5 | 0.116    24.3 | 0.287     9.8 | 0.88x    0.97x          | 2.18x    2.39x         | TRTLLM NVFP4 | 21      | 0/  3/   1.00
64     | 0.165    34.2 | 0.153    36.8 | 0.148    38.0 | 0.386    14.6 | 0.90x    0.97x          | 2.34x    2.52x         | TRTLLM NVFP4 | 29      | 0/  5/   2.00
128    | 0.170    66.5 | 0.157    71.8 | 0.154    73.2 | 0.398    28.3 | 0.91x    0.98x          | 2.34x    2.53x         | TRTLLM NVFP4 | 30      | 0/  8/   3.00
256    | 0.178   126.8 | 0.164   137.3 | 0.164   137.9 | 0.427    52.8 | 0.92x    1.00x          | 2.40x    2.60x         | TRTLLM NVFP4 | 32      | 1/ 11/   7.00
512    | 0.182   247.3 | 0.169   267.6 | 0.174   259.3 | 0.436   103.5 | 0.95x    1.03x          | 2.39x    2.58x         | W4A16        | 32      | 7/ 23/  14.00
1024   | 0.192   470.5 | 0.190   475.4 | 0.316   285.6 | 0.443   203.8 | 1.65x    1.66x          | 2.31x    2.33x         | W4A16        | 32      | 17/ 42/  28.50
2048   | 0.213   848.8 | 0.233   774.5 | 0.326   553.7 | 0.477   378.2 | 1.53x    1.40x          | 2.24x    2.05x         | W4A4         | 32      | 38/ 74/  57.50
4096   | 0.268  1346.3 | 0.362   997.3 | 0.338  1066.3 | 0.529   681.8 | 1.26x    0.94x          | 1.97x    1.46x         | W4A4         | 32      | 81/147/ 117.00
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend
```

</details>

<details>
<summary>Inference per-token run 2 — 13 token counts, 4
backends</summary>

```text
Tokens | CuteDSL W4A4  | CuteDSL W4A16 | TRTLLM NVFP4  | TRTLLM BF16   | Speedup vs TRTLLM NVFP4 | Speedup vs TRTLLM BF16 | Winner       | Active  | Stats
       | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | W4A4     W4A16          | W4A4     W4A16         |              | experts | min/max/median
1      | 0.048     1.8 | 0.039     2.2 | 0.035     2.5 | 0.051     1.7 | 0.73x    0.88x          | 1.06x    1.28x         | TRTLLM NVFP4 | 2       | 0/  1/   0.00
2      | 0.053     3.3 | 0.046     3.9 | 0.046     3.8 | 0.079     2.2 | 0.87x    1.01x          | 1.49x    1.73x         | W4A16        | 4       | 0/  1/   0.00
4      | 0.072     4.9 | 0.062     5.7 | 0.060     5.9 | 0.117     3.0 | 0.83x    0.97x          | 1.62x    1.90x         | TRTLLM NVFP4 | 7       | 0/  1/   0.00
8      | 0.088     8.0 | 0.078     9.0 | 0.074     9.5 | 0.156     4.5 | 0.84x    0.95x          | 1.78x    2.00x         | TRTLLM NVFP4 | 10      | 0/  2/   0.00
16     | 0.097    14.6 | 0.087    16.3 | 0.087    16.3 | 0.190     7.4 | 0.90x    1.00x          | 1.96x    2.19x         | W4A16        | 13      | 0/  2/   0.00
32     | 0.132    21.4 | 0.120    23.5 | 0.116    24.3 | 0.287     9.8 | 0.88x    0.97x          | 2.18x    2.39x         | TRTLLM NVFP4 | 21      | 0/  3/   1.00
64     | 0.165    34.2 | 0.153    36.8 | 0.149    37.9 | 0.387    14.6 | 0.90x    0.97x          | 2.34x    2.53x         | TRTLLM NVFP4 | 29      | 0/  5/   2.00
128    | 0.170    66.4 | 0.157    71.7 | 0.154    73.1 | 0.398    28.3 | 0.91x    0.98x          | 2.34x    2.53x         | TRTLLM NVFP4 | 30      | 0/  8/   3.00
256    | 0.178   126.7 | 0.164   137.2 | 0.164   137.8 | 0.427    52.8 | 0.92x    1.00x          | 2.40x    2.60x         | TRTLLM NVFP4 | 32      | 1/ 11/   7.00
512    | 0.183   247.1 | 0.169   267.4 | 0.174   259.2 | 0.436   103.5 | 0.95x    1.03x          | 2.39x    2.58x         | W4A16        | 32      | 7/ 23/  14.00
1024   | 0.191   471.0 | 0.190   475.1 | 0.317   284.7 | 0.442   203.9 | 1.65x    1.67x          | 2.31x    2.33x         | W4A16        | 32      | 17/ 42/  28.50
2048   | 0.213   848.6 | 0.233   774.4 | 0.326   553.2 | 0.477   378.4 | 1.53x    1.40x          | 2.24x    2.05x         | W4A4         | 32      | 38/ 74/  57.50
4096   | 0.268  1345.8 | 0.363   994.6 | 0.339  1065.5 | 0.528   683.5 | 1.26x    0.93x          | 1.97x    1.46x         | W4A4         | 32      | 81/147/ 117.00
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend
```

</details>

<details>
<summary>Inference per-token run 3 — 13 token counts, 4
backends</summary>

```text
Tokens | CuteDSL W4A4  | CuteDSL W4A16 | TRTLLM NVFP4  | TRTLLM BF16   | Speedup vs TRTLLM NVFP4 | Speedup vs TRTLLM BF16 | Winner       | Active  | Stats
       | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | W4A4     W4A16          | W4A4     W4A16         |              | experts | min/max/median
1      | 0.048     1.8 | 0.040     2.2 | 0.034     2.6 | 0.051     1.7 | 0.72x    0.87x          | 1.06x    1.28x         | TRTLLM NVFP4 | 2       | 0/  1/   0.00
2      | 0.053     3.3 | 0.046     3.9 | 0.046     3.8 | 0.079     2.2 | 0.87x    1.01x          | 1.49x    1.73x         | W4A16        | 4       | 0/  1/   0.00
4      | 0.072     4.9 | 0.064     5.5 | 0.060     5.9 | 0.116     3.0 | 0.83x    0.94x          | 1.61x    1.83x         | TRTLLM NVFP4 | 7       | 0/  1/   0.00
8      | 0.088     8.0 | 0.078     9.0 | 0.074     9.5 | 0.156     4.5 | 0.84x    0.95x          | 1.78x    2.00x         | TRTLLM NVFP4 | 10      | 0/  2/   0.00
16     | 0.097    14.6 | 0.087    16.3 | 0.086    16.4 | 0.190     7.4 | 0.89x    0.99x          | 1.97x    2.20x         | TRTLLM NVFP4 | 13      | 0/  2/   0.00
32     | 0.132    21.4 | 0.120    23.5 | 0.116    24.3 | 0.287     9.8 | 0.88x    0.97x          | 2.18x    2.39x         | TRTLLM NVFP4 | 21      | 0/  3/   1.00
64     | 0.165    34.1 | 0.153    36.8 | 0.149    37.8 | 0.387    14.6 | 0.90x    0.97x          | 2.34x    2.53x         | TRTLLM NVFP4 | 29      | 0/  5/   2.00
128    | 0.170    66.4 | 0.157    71.8 | 0.154    73.2 | 0.400    28.2 | 0.91x    0.98x          | 2.36x    2.55x         | TRTLLM NVFP4 | 30      | 0/  8/   3.00
256    | 0.178   126.9 | 0.164   137.2 | 0.163   138.0 | 0.427    52.8 | 0.92x    0.99x          | 2.40x    2.60x         | TRTLLM NVFP4 | 32      | 1/ 11/   7.00
512    | 0.182   247.7 | 0.168   267.7 | 0.174   258.9 | 0.436   103.4 | 0.96x    1.03x          | 2.39x    2.59x         | W4A16        | 32      | 7/ 23/  14.00
1024   | 0.192   470.6 | 0.190   475.5 | 0.316   285.5 | 0.443   203.8 | 1.65x    1.67x          | 2.31x    2.33x         | W4A16        | 32      | 17/ 42/  28.50
2048   | 0.212   848.9 | 0.233   774.4 | 0.326   554.0 | 0.477   378.1 | 1.53x    1.40x          | 2.25x    2.05x         | W4A4         | 32      | 38/ 74/  57.50
4096   | 0.268  1346.0 | 0.362   997.1 | 0.338  1066.0 | 0.529   682.3 | 1.26x    0.94x          | 1.97x    1.46x         | W4A4         | 32      | 81/147/ 117.00
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend
```

</details>

**Deterministic RL — printed configuration for all three runs:**

```text
DeepSeek-V3 MoE Performance Benchmark
GPU: NVIDIA B300 SXM6 AC
CuteDSL API: Wrapper
Per-token activation: True
Initial activation quantization: True
CuteDSL modes: W4A4 and W4A16; baselines: TRTLLM NVFP4 and TRTLLM BF16
Tensor parallelism simulation: TP=1
CUDA profiler capture: False
CuteDSL finalize: deterministic two-stage
TRTLLM NVFP4 / TRTLLM BF16 finalize: native (unaffected by --no-fused-finalize).
DeepSeek-V3 MoE Benchmark: CuteDSL W4A4/W4A16 vs TRTLLM NVFP4 / TRTLLM BF16 (EP=8, TP=1)
Model: hidden=7168, intermediate=2048, experts=256, top_k=8
EP Config: 32 local experts (simulating 8-way parallelism)
TP Config: intermediate size 2048 (simulating 1-way parallelism)
CUDA Graph: enabled, CUPTI: enabled
Routing bias scale: 0.01 (larger values tend to create expert imbalance)
Timed initial activation quantization for FP4-activation backends: included; W4A16 and TRTLLM BF16 consume BF16 directly
CuteDSL finalize: deterministic two-stage
TRTLLM NVFP4 / TRTLLM BF16 finalize: native (unaffected by --no-fused-finalize).
CUTLASS omitted: it does not consume the per-token activation scale.
```

<details>
<summary>Deterministic RL run 1 — 13 token counts, 4 backends</summary>

```text
Tokens | CuteDSL W4A4  | CuteDSL W4A16 | TRTLLM NVFP4  | TRTLLM BF16   | Speedup vs TRTLLM NVFP4 | Speedup vs TRTLLM BF16 | Winner       | Active  | Stats
       | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | W4A4     W4A16          | W4A4     W4A16         |              | experts | min/max/median
1      | 0.048     1.8 | 0.039     2.2 | 0.036     2.5 | 0.051     1.7 | 0.74x    0.91x          | 1.05x    1.29x         | TRTLLM NVFP4 | 2       | 0/  1/   0.00
2      | 0.059     3.0 | 0.046     3.8 | 0.047     3.7 | 0.078     2.3 | 0.80x    1.03x          | 1.32x    1.70x         | W4A16        | 4       | 0/  1/   0.00
4      | 0.078     4.5 | 0.061     5.8 | 0.061     5.7 | 0.117     3.0 | 0.79x    1.01x          | 1.51x    1.93x         | W4A16        | 7       | 0/  1/   0.00
8      | 0.093     7.6 | 0.076     9.3 | 0.076     9.3 | 0.157     4.5 | 0.82x    1.00x          | 1.69x    2.07x         | TRTLLM NVFP4 | 10      | 0/  2/   0.00
16     | 0.104    13.6 | 0.084    16.9 | 0.087    16.3 | 0.191     7.4 | 0.83x    1.04x          | 1.83x    2.28x         | W4A16        | 13      | 0/  2/   0.00
32     | 0.138    20.4 | 0.117    24.1 | 0.117    24.0 | 0.286     9.9 | 0.85x    1.01x          | 2.07x    2.45x         | W4A16        | 21      | 0/  3/   1.00
64     | 0.175    32.3 | 0.150    37.5 | 0.150    37.5 | 0.386    14.6 | 0.86x    1.00x          | 2.21x    2.57x         | W4A16        | 29      | 0/  5/   2.00
128    | 0.180    62.8 | 0.155    72.9 | 0.156    72.1 | 0.398    28.3 | 0.87x    1.01x          | 2.21x    2.57x         | W4A16        | 30      | 0/  8/   3.00
256    | 0.188   119.6 | 0.167   134.7 | 0.166   136.0 | 0.427    52.8 | 0.88x    0.99x          | 2.26x    2.55x         | TRTLLM NVFP4 | 32      | 1/ 11/   7.00
512    | 0.194   232.1 | 0.167   270.1 | 0.177   254.2 | 0.435   103.6 | 0.91x    1.06x          | 2.24x    2.61x         | W4A16        | 32      | 7/ 23/  14.00
1024   | 0.208   433.7 | 0.185   487.7 | 0.319   282.7 | 0.443   203.7 | 1.53x    1.73x          | 2.13x    2.39x         | W4A16        | 32      | 17/ 42/  28.50
2048   | 0.238   758.6 | 0.224   807.1 | 0.334   540.8 | 0.477   378.0 | 1.40x    1.49x          | 2.01x    2.14x         | W4A16        | 32      | 38/ 74/  57.50
4096   | 0.311  1160.4 | 0.328  1098.6 | 0.350  1030.2 | 0.529   681.9 | 1.13x    1.07x          | 1.70x    1.61x         | W4A4         | 32      | 81/147/ 117.00
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend
```

</details>

<details>
<summary>Deterministic RL run 2 — 13 token counts, 4 backends</summary>

```text
Tokens | CuteDSL W4A4  | CuteDSL W4A16 | TRTLLM NVFP4  | TRTLLM BF16   | Speedup vs TRTLLM NVFP4 | Speedup vs TRTLLM BF16 | Winner       | Active  | Stats
       | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | W4A4     W4A16          | W4A4     W4A16         |              | experts | min/max/median
1      | 0.048     1.8 | 0.039     2.2 | 0.036     2.5 | 0.053     1.7 | 0.74x    0.91x          | 1.10x    1.35x         | TRTLLM NVFP4 | 2       | 0/  1/   0.00
2      | 0.059     3.0 | 0.046     3.8 | 0.047     3.7 | 0.079     2.2 | 0.80x    1.03x          | 1.34x    1.71x         | W4A16        | 4       | 0/  1/   0.00
4      | 0.077     4.5 | 0.060     5.8 | 0.061     5.7 | 0.116     3.0 | 0.79x    1.02x          | 1.50x    1.92x         | W4A16        | 7       | 0/  1/   0.00
8      | 0.093     7.6 | 0.075     9.4 | 0.075     9.4 | 0.160     4.4 | 0.81x    1.00x          | 1.72x    2.13x         | TRTLLM NVFP4 | 10      | 0/  2/   0.00
16     | 0.104    13.5 | 0.083    16.9 | 0.087    16.3 | 0.190     7.4 | 0.83x    1.04x          | 1.83x    2.28x         | W4A16        | 13      | 0/  2/   0.00
32     | 0.138    20.4 | 0.115    24.5 | 0.117    24.1 | 0.287     9.8 | 0.85x    1.02x          | 2.08x    2.49x         | W4A16        | 21      | 0/  3/   1.00
64     | 0.175    32.3 | 0.150    37.5 | 0.150    37.6 | 0.386    14.6 | 0.86x    1.00x          | 2.21x    2.57x         | TRTLLM NVFP4 | 29      | 0/  5/   2.00
128    | 0.180    62.8 | 0.153    73.5 | 0.156    72.1 | 0.400    28.2 | 0.87x    1.02x          | 2.23x    2.61x         | W4A16        | 30      | 0/  8/   3.00
256    | 0.189   119.6 | 0.167   134.7 | 0.165   136.3 | 0.427    52.8 | 0.88x    0.99x          | 2.26x    2.55x         | TRTLLM NVFP4 | 32      | 1/ 11/   7.00
512    | 0.194   232.2 | 0.167   270.2 | 0.177   254.4 | 0.436   103.5 | 0.91x    1.06x          | 2.24x    2.61x         | W4A16        | 32      | 7/ 23/  14.00
1024   | 0.208   433.7 | 0.185   487.9 | 0.320   282.2 | 0.443   203.5 | 1.54x    1.73x          | 2.13x    2.40x         | W4A16        | 32      | 17/ 42/  28.50
2048   | 0.238   759.1 | 0.224   807.1 | 0.333   542.0 | 0.477   378.2 | 1.40x    1.49x          | 2.01x    2.13x         | W4A16        | 32      | 38/ 74/  57.50
4096   | 0.311  1160.6 | 0.328  1098.4 | 0.350  1029.4 | 0.528   682.8 | 1.13x    1.07x          | 1.70x    1.61x         | W4A4         | 32      | 81/147/ 117.00
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend
```

</details>

<details>
<summary>Deterministic RL run 3 — 13 token counts, 4 backends</summary>

```text
Tokens | CuteDSL W4A4  | CuteDSL W4A16 | TRTLLM NVFP4  | TRTLLM BF16   | Speedup vs TRTLLM NVFP4 | Speedup vs TRTLLM BF16 | Winner       | Active  | Stats
       | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | ms  TFLOPS    | W4A4     W4A16          | W4A4     W4A16         |              | experts | min/max/median
1      | 0.048     1.8 | 0.040     2.2 | 0.036     2.4 | 0.053     1.7 | 0.75x    0.90x          | 1.09x    1.31x         | TRTLLM NVFP4 | 2       | 0/  1/   0.00
2      | 0.059     3.0 | 0.046     3.8 | 0.047     3.7 | 0.079     2.2 | 0.81x    1.03x          | 1.34x    1.71x         | W4A16        | 4       | 0/  1/   0.00
4      | 0.077     4.6 | 0.060     5.8 | 0.061     5.7 | 0.117     3.0 | 0.79x    1.01x          | 1.51x    1.93x         | W4A16        | 7       | 0/  1/   0.00
8      | 0.093     7.6 | 0.075     9.4 | 0.075     9.4 | 0.157     4.5 | 0.81x    1.00x          | 1.68x    2.09x         | W4A16        | 10      | 0/  2/   0.00
16     | 0.104    13.6 | 0.083    16.9 | 0.087    16.3 | 0.190     7.4 | 0.84x    1.04x          | 1.83x    2.28x         | W4A16        | 13      | 0/  2/   0.00
32     | 0.139    20.3 | 0.117    24.1 | 0.117    24.1 | 0.287     9.8 | 0.84x    1.00x          | 2.07x    2.46x         | W4A16        | 21      | 0/  3/   1.00
64     | 0.175    32.3 | 0.152    37.0 | 0.150    37.5 | 0.386    14.6 | 0.86x    0.99x          | 2.21x    2.54x         | TRTLLM NVFP4 | 29      | 0/  5/   2.00
128    | 0.180    62.8 | 0.155    72.8 | 0.156    72.2 | 0.398    28.4 | 0.87x    1.01x          | 2.21x    2.57x         | W4A16        | 30      | 0/  8/   3.00
256    | 0.189   119.5 | 0.162   139.1 | 0.166   136.0 | 0.427    52.8 | 0.88x    1.02x          | 2.26x    2.63x         | W4A16        | 32      | 1/ 11/   7.00
512    | 0.194   232.3 | 0.167   270.1 | 0.177   254.3 | 0.436   103.3 | 0.91x    1.06x          | 2.25x    2.61x         | W4A16        | 32      | 7/ 23/  14.00
1024   | 0.208   433.5 | 0.185   487.5 | 0.322   280.3 | 0.443   203.4 | 1.55x    1.74x          | 2.13x    2.40x         | W4A16        | 32      | 17/ 42/  28.50
2048   | 0.238   759.5 | 0.223   807.2 | 0.333   542.3 | 0.477   378.1 | 1.40x    1.49x          | 2.01x    2.14x         | W4A16        | 32      | 38/ 74/  57.50
4096   | 0.311  1159.4 | 0.329  1097.9 | 0.351  1029.3 | 0.528   683.4 | 1.13x    1.07x          | 1.70x    1.61x         | W4A4         | 32      | 81/147/ 117.00
Speedup > 1.0 means that CuTe DSL mode is faster than the comparison backend
```

</details>

### Derived performance summary

Latencies are **medians of three fresh-process runs**, calculated from
unrounded captures and displayed in milliseconds to three decimal
places. Each **W4A16 speedup** is the named TRTLLM baseline's median
latency divided by the CuTe DSL W4A16 median latency at that token
count. These are ratios of medians; values above 1 mean W4A16 is faster;
values below 1 mean W4A16 is slower. Both NVFP4 and pure BF16
comparisons are explicit.

**Inference per-tensor — median of three runs**

| Tokens | CuTe W4A4 (ms) | CuTe W4A16 (ms) | CUTLASS NVFP4 (ms) |
TRTLLM NVFP4 (ms) | TRTLLM BF16 (ms) | W4A16 speedup over TRTLLM NVFP4 |
W4A16 speedup over TRTLLM BF16 |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 1 | 0.031 | 0.040 | 0.048 | 0.026 | 0.051 | 0.65x | 1.29x |
| 2 | 0.040 | 0.046 | 0.058 | 0.036 | 0.079 | 0.79x | 1.72x |
| 4 | 0.057 | 0.063 | 0.070 | 0.051 | 0.117 | 0.81x | 1.85x |
| 8 | 0.073 | 0.079 | 0.086 | 0.064 | 0.157 | 0.81x | 1.99x |
| 16 | 0.083 | 0.085 | 0.097 | 0.076 | 0.192 | 0.89x | 2.25x |
| 32 | 0.114 | 0.118 | 0.127 | 0.106 | 0.285 | 0.89x | 2.41x |
| 64 | 0.147 | 0.151 | 0.159 | 0.140 | 0.388 | 0.92x | 2.56x |
| 128 | 0.150 | 0.158 | 0.164 | 0.144 | 0.399 | 0.91x | 2.53x |
| 256 | 0.158 | 0.164 | 0.173 | 0.154 | 0.428 | 0.93x | 2.60x |
| 512 | 0.160 | 0.169 | 0.179 | 0.164 | 0.435 | 0.97x | 2.58x |
| 1024 | 0.165 | 0.190 | 0.197 | 0.295 | 0.443 | 1.56x | 2.34x |
| 2048 | 0.177 | 0.233 | 0.223 | 0.299 | 0.476 | 1.28x | 2.04x |
| 4096 | 0.217 | 0.363 | 0.281 | 0.309 | 0.529 | 0.85x | 1.46x |

**Inference per-token — median of three runs**

| Tokens | CuTe W4A4 (ms) | CuTe W4A16 (ms) | TRTLLM NVFP4 (ms) | TRTLLM
BF16 (ms) | W4A16 speedup over TRTLLM NVFP4 | W4A16 speedup over TRTLLM
BF16 |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 1 | 0.048 | 0.040 | 0.034 | 0.051 | 0.87x | 1.28x |
| 2 | 0.053 | 0.046 | 0.046 | 0.079 | 1.01x | 1.73x |
| 4 | 0.072 | 0.062 | 0.060 | 0.116 | 0.97x | 1.88x |
| 8 | 0.088 | 0.078 | 0.074 | 0.156 | 0.95x | 2.00x |
| 16 | 0.097 | 0.087 | 0.086 | 0.190 | 1.00x | 2.20x |
| 32 | 0.132 | 0.120 | 0.116 | 0.287 | 0.97x | 2.39x |
| 64 | 0.165 | 0.153 | 0.149 | 0.387 | 0.97x | 2.52x |
| 128 | 0.170 | 0.157 | 0.154 | 0.398 | 0.98x | 2.53x |
| 256 | 0.178 | 0.164 | 0.164 | 0.427 | 1.00x | 2.60x |
| 512 | 0.182 | 0.169 | 0.174 | 0.436 | 1.03x | 2.58x |
| 1024 | 0.192 | 0.190 | 0.316 | 0.443 | 1.66x | 2.33x |
| 2048 | 0.213 | 0.233 | 0.326 | 0.477 | 1.40x | 2.05x |
| 4096 | 0.268 | 0.362 | 0.338 | 0.529 | 0.94x | 1.46x |

**Deterministic RL — median of three runs**

| Tokens | CuTe W4A4 (ms) | CuTe W4A16 (ms) | TRTLLM NVFP4 (ms) | TRTLLM
BF16 (ms) | W4A16 speedup over TRTLLM NVFP4 | W4A16 speedup over TRTLLM
BF16 |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 1 | 0.048 | 0.039 | 0.036 | 0.053 | 0.91x | 1.34x |
| 2 | 0.059 | 0.046 | 0.047 | 0.079 | 1.03x | 1.71x |
| 4 | 0.077 | 0.060 | 0.061 | 0.117 | 1.01x | 1.93x |
| 8 | 0.093 | 0.075 | 0.075 | 0.157 | 1.00x | 2.09x |
| 16 | 0.104 | 0.083 | 0.087 | 0.190 | 1.04x | 2.28x |
| 32 | 0.138 | 0.117 | 0.117 | 0.287 | 1.00x | 2.46x |
| 64 | 0.175 | 0.150 | 0.150 | 0.386 | 1.00x | 2.57x |
| 128 | 0.180 | 0.155 | 0.156 | 0.398 | 1.01x | 2.57x |
| 256 | 0.189 | 0.167 | 0.166 | 0.427 | 0.99x | 2.55x |
| 512 | 0.194 | 0.167 | 0.177 | 0.436 | 1.06x | 2.61x |
| 1024 | 0.208 | 0.185 | 0.320 | 0.443 | 1.73x | 2.40x |
| 2048 | 0.238 | 0.224 | 0.333 | 0.477 | 1.49x | 2.13x |
| 4096 | 0.311 | 0.328 | 0.350 | 0.528 | 1.07x | 1.61x |

- **Inference per-tensor:** W4A16 speedup 0.65–1.56x over TRTLLM NVFP4
(2/13 faster); 1.29–2.60x over TRTLLM BF16 (13/13 faster).
- **Inference per-token:** W4A16 speedup 0.87–1.66x over TRTLLM NVFP4
(4/13 faster); 1.28–2.60x over TRTLLM BF16 (13/13 faster).
- **Deterministic RL:** W4A16 speedup 0.91–1.73x over TRTLLM NVFP4 (9/13
faster); 1.34–2.61x over TRTLLM BF16 (13/13 faster).

### BF16 performance investigation

**Finding:** the W4A16 advantage is present in GPU expert computation.
TRTLLM BF16 tuning is active; exhaustive native-tactic checks on the
actual inputs do not close the gap. Preparation, tuning, L2 flushing and
host dispatch are outside the reported CUPTI GPU span. Independent
single-call graph events reproduce the ordering. These diagnostics
supplement the unchanged nine sweeps.

**Provenance:** profiles, actual-input tactic sweep and event controls
use `2079fa00c4a2a0fb2e95bb284978a703653a4830` on the same retained
B300/CUDA 13.2 image above. Nsight Systems 2026.2.1, GPU 0; tactic
diagnostics GPU 1, serialized with GPU 0 work. Initial cache/default
audit uses `a3fce87d5beec29775e9bc3917df99ba6bc4db9c`. Naming commit
`d0caa842` and profiling-only commit `2079fa00` are separate; normal
benchmark functions/timing remain unchanged from the refactor validation
source.

**Autotuning:** 2,852 successful native candidate profiles across 21
token buckets, zero failures; all ten measured warmup/capture dispatches
used nonfallback cache hits with tuning disabled. The selected tactic
reaches the native launcher. At 32 tokens, all 352 actual-input
candidates give a best 284.5635 µs versus 287.3155 µs for the selected
baseline (about 1%, single sweep). At 4096, the selected `(128,0)` is
also the exhaustive eight-candidate winner at 529.3520 µs. Default `-1`
is ~15% slower there. The v1 tuner's synthetic routing activates 11/30
local experts at 32/4096, versus 21/32 on actual inputs; shorter
synthetic probe times therefore are not the final benchmark latency. The
exhaustive actual-input sweep checks this selection concern directly.

TRTLLM BF16 tuned/default controls (µs; each cell is a 100-iteration
median; 3 alternating pairs):

```csv
tokens,tuned_tactic,repeat,tuned_us,default_us
32,8:92,1,286.9315,287.1400
32,8:92,2,287.1560,287.0915
32,8:92,3,287.0440,287.0440
4096,128:0,1,528.9665,609.1280
4096,128:0,2,530.0870,610.4075
4096,128:0,3,531.5110,611.6720
```

All actual-input native candidates (median of 100 iterations, µs; one
sample each). Each CSV row lists latencies in ascending contiguous
config-ID order; `(tileN, config)` is the native tactic. This includes
every 352/8 enumerated candidate plus default.

```text
tokens=32; default(-1)=286.8350
tile=8; config=0..143
295.8595,286.0840,297.8440,285.7475,299.2360,285.5240,295.9080,297.4760,286.0515,299.2035,286.1795,285.4120,297.0920,287.0435,298.6920,286.5315,300.6760,286.2120,296.9640,298.5000,286.8360,300.1955,286.5155,286.3720,295.2840,285.3795,296.8685,285.3000,299.1075,284.6115,295.2520,296.9475,285.4115,299.1080,284.8520,284.5635,296.2920,286.4840,298.1160,286.3075,300.0350,285.9240,296.1640,298.5960,286.8840,300.0355,286.2435,285.6995,297.1560,286.8360,298.8200,286.7560,300.6760,286.3080,296.5795,298.5475,286.8035,300.8355,286.7235,286.1800,296.7555,286.9640,298.3240,287.0435,300.3080,286.3885,296.9475,297.9720,287.0115,300.1160,286.6915,286.4990,296.2600,285.9395,297.4600,285.8280,299.6360,285.6360,295.9720,297.3800,286.1155,299.5075,285.6360,285.3955,297.2680,287.2360,298.4360,286.8200,300.6600,286.3555,297.1070,298.7720,286.9630,301.0115,286.7235,286.4840,295.3160,285.2355,296.7560,285.2200,299.2040,284.9795,295.3315,296.9640,285.4600,299.2995,285.0440,284.5800,296.3880,286.6440,297.8755,286.3395,299.7320,285.7800,296.1955,297.9715,287.1240,300.1160,286.1960,285.9715,297.1395,287.0595,299.0280,286.9150,300.9635,286.3720,297.1560,298.4520,286.9155,300.9960,286.6755,286.5800,296.9000,287.0920,298.2125,287.1080,300.1795,286.5635,296.5800,298.5800,287.0280,300.3085,286.8520,286.2920
tile=16; config=0..143
292.6915,294.5480,285.8280,296.8680,285.6355,285.1880,292.8355,285.6040,294.7075,296.8840,285.6200,285.3160,293.3160,295.2365,286.3875,297.7965,286.3235,286.0680,293.0440,286.6600,295.0760,297.4760,286.4200,286.2920,291.9400,293.9885,285.0910,296.1645,285.0600,284.7080,292.0835,284.9640,293.8280,296.4360,285.1070,284.7230,292.6760,294.9800,285.9240,297.1880,286.2595,285.3320,292.7240,285.9880,294.7080,296.8680,286.0040,285.5075,293.7630,295.8605,287.0440,298.0835,287.0920,286.5960,293.5720,286.7880,295.6520,297.4760,287.0120,286.1800,293.2840,295.4600,286.8670,297.9560,286.6115,286.2115,293.0435,286.3720,295.3805,297.2840,286.9325,286.0675,292.2435,294.4520,285.6040,296.4360,285.8760,285.1560,292.7245,285.5240,294.7560,296.9000,285.7320,285.3005,293.2845,295.3000,286.3245,297.5245,286.4360,286.5000,293.1080,286.4680,295.2520,297.5875,286.7240,286.1480,291.8760,294.1960,285.1080,296.0360,284.9640,284.8200,291.6040,285.1235,294.0360,296.2920,285.3160,284.7555,292.6440,294.8200,285.8920,296.9640,286.0520,285.1880,292.7560,285.9550,294.5955,297.2520,286.0840,285.7315,293.7000,295.8920,286.8520,298.2760,286.9000,286.9320,293.7320,286.8520,295.3640,297.7485,287.1720,286.6755,293.3965,295.4440,286.3725,297.3800,286.9160,286.1795,293.5725,286.5480,295.1400,297.5710,286.5950,286.2915
tile=32; config=0..63
292.4360,286.0520,295.0600,285.7955,292.2285,295.1080,285.5555,286.2440,294.3400,287.7800,297.5880,287.7320,294.1960,297.2200,287.8120,287.8920,293.0760,286.9160,296.5960,286.7720,292.8675,296.4680,286.8035,286.7240,293.6680,287.7165,297.1715,287.5720,293.5565,296.9160,287.6040,287.4920,292.1795,286.2110,295.2680,285.7475,292.1635,295.4600,286.0200,286.0675,294.1480,288.0195,297.5405,287.7955,294.1480,297.2200,287.6845,288.0680,293.2840,286.9160,296.1320,286.7075,293.0920,296.3240,286.9320,286.9800,293.8600,287.7960,296.7075,287.4290,293.5720,296.9160,287.3000,287.6835
tokens=4096; default(-1)=609.7365
tile=64; config=0..3
611.4960,611.9765,610.2970,611.5760
tile=128; config=0..3
529.3520,529.5590,530.8710,530.3430
```

**Nsight Systems complete device breakdown (µs):** 20 cold-L2, one-call
graph replays per case after tuning and three uncaptured replays. Every
non-dash activity cell is the mean of 20 instances; both temporary-zero
kernels are shown separately. All target kernels/memsets are accounted
for inside synchronized `moe` NVTX ranges; each contains one
`cudaGraphLaunch`. The 20 separately labeled L2 flushes are excluded.
Kernel sums are diagnostic, not latency: [programmatic dependent
launch](https://docs.nvidia.com/cuda/cuda-programming-guide/04-special-topics/programmatic-dependent-launch.html)
allows dependent kernel intervals to overlap. GPU span measures the
first activity start through last activity end; uncovered gaps use the
union of intervals. Inference profiles cover both inference activation
variants because those flags do not change W4A16 or TRTLLM BF16; RL is
separately profiled.

**Inference:**

| Activity / metric | CuTe W4A16 / 32 | TRTLLM BF16 / 32 | CuTe W4A16 /
4096 | TRTLLM BF16 / 4096 |
| --- | ---: | ---: | ---: | ---: |
| Top-k routing | 3.150 | 3.346 | 9.635 | 9.214 |
| Temporary zero 1 | 0.587 | — | 0.672 | — |
| Temporary zero 2 | 0.628 | — | 0.702 | — |
| Route map | 3.709 | 3.171 | 6.832 | 6.339 |
| BF16 permute | 4.648 | — | 22.986 | — |
| GEMM1 + SwiGLU | 67.812 | 189.739 | 164.956 | 331.377 |
| Output zero | 0.906 | — | 9.722 | — |
| GEMM2 | 36.946 | 89.677 | 147.033 | 150.361 |
| Finalize | — | 5.779 | — | 38.018 |
| GPU activity sum | 118.386 | 291.712 | 362.538 | 535.309 |
| GPU span | 118.522 | 285.562 | 359.142 | 527.871 |
| Uncovered GPU gaps | 0.668 | 0.000 | 0.644 | 0.000 |
| Graph launch CPU API (excluded) | 9.867 | 8.062 | 7.405 | 8.048 |
| L2 flush (excluded) | 67.826 | 67.925 | 67.882 | 67.810 |

**Deterministic RL:**

| Activity / metric | CuTe W4A16 / 32 | TRTLLM BF16 / 32 | CuTe W4A16 /
4096 | TRTLLM BF16 / 4096 |
| --- | ---: | ---: | ---: | ---: |
| Top-k routing | 3.094 | 3.283 | 9.622 | 9.283 |
| Temporary zero 1 | 0.592 | — | 0.677 | — |
| Temporary zero 2 | 0.590 | — | 0.694 | — |
| Route map | 3.578 | 3.014 | 7.237 | 6.586 |
| BF16 permute | 4.526 | — | 23.088 | — |
| GEMM1 + SwiGLU | 68.505 | 188.811 | 165.900 | 331.810 |
| Output zero | — | — | — | — |
| GEMM2 | 43.343 | 89.194 | 110.439 | 150.190 |
| Finalize | 5.880 | 5.755 | 31.789 | 38.095 |
| GPU activity sum | 130.108 | 290.058 | 349.446 | 535.964 |
| GPU span | 114.262 | 284.682 | 326.137 | 528.484 |
| Uncovered GPU gaps | 0.365 | 0.000 | 0.329 | 0.000 |
| Graph launch CPU API (excluded) | 7.163 | 6.141 | 10.132 | 8.223 |
| L2 flush (excluded) | 67.928 | 67.817 | 67.880 | 67.842 |

Kernel mapping: top-k=`deepseek_v3_topk_kernel` / `routingMainKernel`;
route map=`routingIndicesClusterKernel` / `routingIndicesCoopKernel`;
temporary zeros=`vectorized_elementwise_kernel`;
permute/unpermute=`moePermuteKernel` / `moeUnpermuteKernel`; CuTe
GEMMs=`Sm100W4A16GroupedGemmKernel` in launch order; TRTLLM BF16
GEMMs=`bmm_Bfloat16_Bfloat16Bfloat16...` (FC1 has `swiGlu`); native
finalize=`finalizeKernel` / `finalizeKernelVecLoad`. CuTe inference
GEMM2 includes atomic finalize; RL uses the separate unpermute row. At
32 tokens both GEMMs explain the gap; at 4096 the largest difference is
GEMM1, with native finalize also contributing. TRTLLM BF16 has no
uncovered device gaps in these traces.

**Independent timer control:** same tuned callable, one call per graph,
100 samples after 10 warmups; zero a 2×L2 byte buffer and synchronize
before each start event, then `start.record(); graph.replay();
end.record(); end.synchronize()`. CUPTI separately measures the same
prepared callable. Every case passed with finite outputs. Table contains
every case's median in µs. Event intervals add roughly 4–6 µs but retain
the gap; host submission cannot explain the BF16 slowdown. Warm-cache
results are sequential diagnostics, not an isolated cache speedup claim.
Ordinary `--no-cupti` is not this control: it uses ten-call graphs and
rotates only input kwargs, leaving closure-held weights unrotated.

| Config | Tokens | Backend | Cold CUPTI | Cold events | Warm events |
| --- | ---: | --- | ---: | ---: | ---: |
| inference | 1 | CuTe W4A16 | 38.9440 | 42.9440 | 39.8240 |
| inference | 1 | TRTLLM BF16 | 50.7525 | 56.8640 | 46.5280 |
| inference | 32 | CuTe W4A16 | 118.9450 | 124.4480 | 113.4080 |
| inference | 32 | TRTLLM BF16 | 286.0665 | 292.3360 | 276.1920 |
| inference | 4096 | CuTe W4A16 | 361.9550 | 366.7200 | 378.1440 |
| inference | 4096 | TRTLLM BF16 | 528.9640 | 533.6480 | 543.2800 |
| rl | 1 | CuTe W4A16 | 40.1280 | 45.8080 | 42.1760 |
| rl | 1 | TRTLLM BF16 | 53.0885 | 58.8960 | 48.0480 |
| rl | 32 | CuTe W4A16 | 115.0410 | 120.3360 | 112.3520 |
| rl | 32 | TRTLLM BF16 | 286.5785 | 292.5920 | 276.2240 |
| rl | 4096 | CuTe W4A16 | 327.4590 | 333.0560 | 355.2640 |
| rl | 4096 | TRTLLM BF16 | 528.6275 | 533.4560 | 542.9120 |

### Nsight Compute: fixed normal-tuned tactics

- **Scope:** inference per-token, EP8/TP1, B300 GPU 0, NCU 2026.1.1,
source `2079fa00`. Tactics were recorded during normal tuning before NCU
injection, then fixed by a diagnostic helper; all captured calls had
tuning disabled. Each report contains exactly FC1 (with SwiGLU) and FC2,
nine replay passes each. Node replay flushes caches and isolates PDL
kernels: these durations are diagnostics, not additive end-to-end
latency.
- **Tactics:** TRTLLM BF16: `(8, 37)` at 32; `(128, 1)` at 4096. CuteDSL
W4A16 uses the same tactic for both GEMMs: `((128, 8, 256), (2, 1),
True)` at 32; `((256, 128, 256), (2, 1), True)` at 4096.

Raw counters below: R/W = DRAM bytes; D/T/S = DRAM throughput /
tensor-pipe activity / SM throughput (% of sustained elapsed peak);
LD/ST = local-memory sectors; warps = active warps per active SM cycle;
A/T = achieved/theoretical occupancy (%).

| Tokens | Backend | GEMM | R bytes | W bytes | ns | D/T/S % | LD/ST |
Warps | Occupancy A/T % | Registers/thread | Shared bytes/block |
| ---: | --- | --- | ---: | ---: | ---: | --- | --- | ---: | --- | ---:
| ---: |
| 32 | TRTLLM BF16 | FC1 | 1233656832 | 3507200 | 176896 |
91.17/2.42/25.37 | 0/0 | 11.33 | 17.70/18.75 | 168 | 181904 |
| 32 | TRTLLM BF16 | FC2 | 616784896 | 4100096 | 93760 |
86.35/2.27/23.87 | 0/0 | 7.95 | 12.42/12.50 | 255 | 216720 |
| 4096 | TRTLLM BF16 | FC1 | 2064285184 | 17972224 | 323040 |
84.03/40.83/45.78 | 0/0 | 10.84 | 16.94/18.75 | 168 | 187032 |
| 4096 | TRTLLM BF16 | FC2 | 960271104 | 50072064 | 152864 |
86.17/44.00/48.40 | 0/0 | 6.98 | 10.91/12.50 | 255 | 219800 |
| 32 | CuteDSL W4A16 | FC1 | 349258752 | 3499264 | 65632 |
70.11/6.61/53.35 | 0/0 | 15.86 | 24.77/25.00 | 128 | 215040 |
| 32 | CuteDSL W4A16 | FC2 | 174409728 | 3022592 | 37504 |
61.78/5.74/49.50 | 0/0 | 15.61 | 24.39/25.00 | 128 | 206880 |
| 4096 | CuteDSL W4A16 | FC1 | 637545728 | 13989120 | 165696 |
51.26/87.20/87.81 | 0/0 | 15.84 | 24.75/25.00 | 128 | 223232 |
| 4096 | CuteDSL W4A16 | FC2 | 322100992 | 7225344 | 148096 |
28.99/44.90/46.44 | 0/0 | 15.06 | 23.53/25.00 | 128 | 215168 |

Remaining launch counters (all eight kernels): occupancy limits = 32
blocks, 1 block by registers, 1 by shared memory; warp limit = 5/8
blocks for BF16 FC1/FC2 and 4 for W4A16. Cluster dimensions = `(2,1,1)`,
except BF16 at 32 uses `(1,1,1)`. `occupancy_cluster_pct` = 3.12%;
`occupancy_cluster_gpu_pct` = 0.59/0.39% for BF16 FC1/FC2 and 0.78% for
both W4A16 kernels (launch estimates, not measured GPU activity). Full
raw CSV/report metadata is retained.

**Interpretation:** at 4096, BF16 still reaches 84–86% DRAM throughput
with 41–44% tensor activity; W4A16 FC1 reaches 87% tensor activity. BF16
reads 3.025 GB versus W4A16's 0.960 GB. EP8 leaves 3715 local
assignments across 32 experts (116/expert); 4096 is not each expert's
GEMM M. No local load/store traffic was measured in any kernel.
Registers and shared memory both limit residency to one block/SM; low
occupancy alone does not establish an avoidable defect. These counters
support a bandwidth explanation and do not demonstrate a spilling or
launch-overhead bug.

Public CLI equivalent on `2079fa00`: TRTLLM BF16 at 32 passed normal
cache save then fresh-process `--no-autotune` load (both exit 0, target
cache hit, no retuning, unchanged cache hash). The measured counters
above used the helper-frozen tactics, not this smoke cache. Run in the
same inference environment with NVFP4 overrides unset. Require a
target-operation `Config cache hit`; missing/incompatible caches can
otherwise fall back.

```bash
export CUDA_VISIBLE_DEVICES=0 FLASHINFER_DISABLE_VERSION_CHECK=1 MAX_JOBS=8 FLASHINFER_LOGGING_LEVEL=info
m=(dram__bytes_{read,write}.sum {dram__throughput,sm__throughput,sm__pipe_tensor_cycles_active}.avg.pct_of_peak_sustained_elapsed gpu__time_duration.sum l1tex__t_sectors_pipe_lsu_mem_local_op_{ld,st}.sum sm__warps_active.avg.{per_cycle_active,pct_of_peak_sustained_active} sm__maximum_warps_per_active_cycle_pct launch__{registers_per_thread,shared_mem_per_block,occupancy_limit_{blocks,registers,shared_mem,warps},cluster_dim_{x,y,z},occupancy_cluster_pct,occupancy_cluster_gpu_pct})
for n in 32 4096; do
  for b in cute-dsl-w4a16 trtllm-bf16; do
    a=(benchmarks/bench_moe_deepseek.py --num-tokens "$n" --ep 8 --tp 1 --routing-input-mode logits --use-per-token-activation --include-activation-quant --profile-cuda --profile-backend "$b" --profile-iters 1 --cache "$b-$n.json")
    python "${a[@]}"
    test -s "$b-$n.json" || exit 1
    ncu --profile-from-start off --replay-mode kernel --graph-profiling node --nvtx --nvtx-include moe/ --kernel-name-base demangled --rename-kernels off --kernel-name 'regex:.*(Sm100W4A16GroupedGemmKernel|bmm_Bfloat16_Bfloat16Bfloat16_).*' --launch-count 2 --cache-control all --clock-control none --metrics "$(IFS=,; echo "${m[*]}")" --csv --page raw --print-units base --export "$b-$n" python "${a[@]}" --no-autotune
  done
done
```

**Why 4096 remains bandwidth-sensitive:** the actual EP8 routing has
3715/32 = 116.09 rows per expert. Useful BF16 GEMM FLOPs divided by
unique weight bytes give 116.09 FLOP/byte; including minimum activation
traffic gives about 108–112. The advertised dense BF16 rate is 36
PFLOPS/node ÷ 2 ÷ 8 = 2.25 PFLOPS/GPU ([NVIDIA HGX
specifications](https://www.nvidia.com/en-us/data-center/hgx/)); with [8
TB/s
HBM](https://docs.nvidia.com/enterprise-reference-architectures/hgx-ai-factory/latest/components.html),
the ideal crossover is about 281 FLOP/byte. These are analytical peak
bounds, supported here by the measured DRAM counters. Tile128 pads to 40
tiles/5120 slots (72.56% useful rows). The 3.025 GB measured BF16 reads
are close to 2.819 GB unique weights and below 3.523 GB if every token
tile reread them. Compression moves W4A16 GEMM1 toward compute
saturation; equal tensor-core input precision does not give equal memory
traffic.

**Reproduce the eight Nsight traces** from the implementation checkout
and image above. Output files remain on the retained devbox; no
compilation caches are removed.

```bash
git checkout 2079fa00c4a2a0fb2e95bb284978a703653a4830
export CUDA_VISIBLE_DEVICES=0 FLASHINFER_DISABLE_VERSION_CHECK=1 MAX_JOBS=8
for mode in inference rl; do
  unset FLASHINFER_NVFP4_4OVER6 FLASHINFER_NVFP4_4OVER6_E4M3_USE_256
  unset FLASHINFER_NVFP4_4OVER6_ERR_MODE FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH
  unset FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH
  extra=()
  if [[ $mode == rl ]]; then
    export FLASHINFER_NVFP4_4OVER6=1 FLASHINFER_NVFP4_4OVER6_E4M3_USE_256=1
    export FLASHINFER_NVFP4_4OVER6_ERR_MODE=MSE FLASHINFER_NVFP4_4OVER6_ERR_USE_FAST_MATH=1
    export FLASHINFER_DISABLE_FP4_QUANT_FAST_MATH=1
    extra=(--no-fused-finalize)
  fi
  for n in 32 4096; do
    for backend in cute-dsl-w4a16 trtllm-bf16; do
      nsys profile --sample=none --cpuctxsw=none --trace=cuda,nvtx \
        --cuda-graph-trace=node --capture-range=cudaProfilerApi \
        --capture-range-end=stop -o "$mode-$backend-t$n" \
        python3 benchmarks/bench_moe_deepseek.py --num-tokens "$n" --ep 8 \
        --warmup 10 --iters 100 --use-per-token-activation --include-activation-quant \
        --profile-cuda --profile-backend "$backend" --profile-iters 20 "${extra[@]}"
      nsys export --type=sqlite -o "$mode-$backend-t$n.sqlite" "$mode-$backend-t$n.nsys-rep"
    done
  done
done
```

Aggregate all GPU activities contained by each synchronized `moe` NVTX
range, including target memsets; verify 20 ranges and one graph launch
per range. Report per-kernel `sum(end-start)/20`, per-range GPU
`max(end)-min(start)`, and CPU graph-launch duration separately. Exclude
`l2_flush`; do not sum kernel durations as wall-clock latency. These are
single-GPU local-expert diagnostics, not distributed or full-model
speedups.

### Interpretation and limits

- Single-GPU EP8 shard shapes: no dispatch/combine, all-gather,
all-reduce, network or full-model runtime is timed.
- Both TRTLLM arms use native finalize in every configuration. Native
logits routing stores selected weights in BF16; CuTe route weights are
FP32. This is not a numerical-parity claim between BF16/quantized paths
or proof of RL-training determinism.
- New per-tensor runs also remeasure the unaffected W4A16/BF16 paths.
Differences across separate sweeps are not attributed solely to
activation scaling. Coverage is B300/SM103 and the stated shapes; other
architectures and distributed execution were not tested.

## 🔍 Related Issues

Related implementation and benchmark configurations: #4048.

## 🚀 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

- [x] I have installed `pre-commit` by running `pip install pre-commit`
(or used your preferred method).
- [x] 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](https://pre-commit.com/).

## 🧪 Tests

- [ ] Tests have been added or updated as needed.
- [ ] All tests are passing (`unittest`, etc.).

- **Original measured source:** seven existing BF16 GPU tests passed
(310 deselected); eight independent reference cases passed with
unchanged `rtol=atol=0.03`, three exact eager repeats and three exact
CUDA-graph replays per case. BF16 CLI/profiler smoke passed. [Complete
original test output, helper and
commands](https://github.com/flashinfer-ai/flashinfer/pull/4985#issuecomment-5549485234).
- **Shared-helper refactor `a3fce87d`:** 43 paired GPU cases matched
measured source `1ac2233d` for public entry points, bound kernel
arguments, finalized output bytes and timing inputs; renamed selectors
and NVFP4 profiler smoke passed; an excluded profiling backend is
rejected with exit 2. Autotuning was disabled for these bounded
equivalence checks. [Complete refactor validation and
reproduction](https://github.com/flashinfer-ai/flashinfer/pull/4985#issuecomment-5549433238).
- **Later head `2079fa00`:** printed-name fixtures and external-profiler
NVTX ranges are validated separately; the five benchmark functions and
non-profile timing branch remain AST-identical to the 43-case refactor
source. Those 43 cases are attributed to the refactor commit above.
- **Static scope:** file-scoped pre-commit hooks, Ruff, formatting,
Python compilation and `git diff --check` passed. Mypy is skipped by the
configured hook because this benchmark is outside its `flashinfer/`
scope. No upstream test files were changed; all-files hooks and the full
repository test suite were not run. These correctness checks do not
replace the nine performance sweeps.

## 🔬 Experimental Track

<!-- Only for PRs submitted under the experimental policy
(CONTRIBUTING.md → "Experimental APIs and Backends").
     Leave this section untouched for normal PRs. -->

- [ ] This PR is **experimental**: it adds or changes code under
`flashinfer/experimental/` and/or an `@flashinfer_experimental_api`.
Tracking issue: #
- [ ] The tracking issue names an owner, the reason for the experimental
path, and a graduation plan with a target release.
- [ ] Core changes are limited to a thin entry point (signature, shared
validation, feature-gate check, backend selection, handoff).
- [ ] Tests live in `tests/experimental/` and were validated on the
intended hardware; a runnable example is included.
- [ ] Nothing is registered in `flashinfer/aot.py`, and no experimental
backend is reachable from `backend="auto"` without
`FLASHINFER_ALLOW_EXPERIMENTAL_AUTO_BACKENDS=1`. (Calling an
`@flashinfer_experimental_api` or naming a backend explicitly is itself
the opt-in and needs no environment variable.)
- [ ] **Test scope declared below.** The experimental CI lane runs
exactly these targets, so keep them as narrow as the change allows.

<!-- Required for experimental PRs. Replace the commented lines below
with your targets.
Do not delete the fence or change its `experimental-tests` tag — the
experimental-track
watcher reads it verbatim to decide which targets to ask CI for. -->

```experimental-tests
# One target per line: a directory or a file. (A pytest ::selector is not
# supported -- the sharding runner cannot consume one.) Must be under
# tests/experimental/ and must exist. Delete these comment lines and add yours, e.g.
#
#   tests/experimental/test_my_backend.py
#   tests/experimental/my_backend/
#
# Declaring the whole tree (tests/experimental/) is allowed but means every
# experimental PR pays for every other feature's tests, in every matrix cell.
```

## Reviewer Notes

Please review the precision-specific TRTLLM weight/scale arguments,
shared routing and timing boundary, explicit backend/finalize labels,
and the three configuration definitions. All nine raw tables and both
W4A16 baseline speedup columns are above; complete validation output is
linked in Tests. Unchecked all-files/full-suite items reflect the stated
scope.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Unified TensorRT-LLM benchmarking for NVFP4 and BF16 precision modes.
  * Added support for logits and pre-routed execution scenarios.
* Added the `trtllm-bf16` backend to benchmark selection, validation,
tables, speedup reporting, and command-line help.
* Improved consistency for routed workload comparisons across
TensorRT-LLM precision modes.

* **Bug Fixes**
* Profile-backend selection now rejects values not included in the
selected backend list.
* Benchmark status and winner labels now clearly distinguish NVFP4 and
BF16 results.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants