Skip to content

feat(cake_softmax): add optimized Blackwell softmax - #4282

Open
yyihuang wants to merge 14 commits into
flashinfer-ai:mainfrom
yyihuang:perf/blackwell-softmax-loom
Open

yyihuang wants to merge 14 commits into
flashinfer-ai:mainfrom
yyihuang:perf/blackwell-softmax-loom

Conversation

@yyihuang

@yyihuang yyihuang commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR adds an optimized Blackwell FP32 softmax dispatcher behind the existing flashinfer.sampling.softmax API for SM100 and SM103. The public ABI and output semantics stay unchanged: 2-D CUDA logits, temperature absent/scalar/per-row, PDL support, and a fresh non-aliasing output. Unsupported devices and launches retain the existing OnlineSoftmax fallback.

The current head is f0edac69e1c4d299c14fa95b7a0073a172203cf9. It contains the warp, rowwise, cooperative-bootstrap, and MR515-derived exp2/512-thread/vec4 routes. The device kernels and dispatcher are unchanged from the source-bound 082c7f54 GB300 qualification; f0edac69 fixes the strict LogitsPipe regression by making FusedTemperatureSoftmaxOp call the public architecture-dispatched sampling.softmax path with the same temperature and PDL arguments, instead of bypassing it through the generic module. It also removes the now-unused workspace and adds an independent Torch-oracle regression for the three failing per-row-temperature shapes.

Dispatch policy

The original PR4282 warp, rowwise, and bootstrap regions are preserved. Route 4 is restricted to the SM103 cases where same-session paired CUPTI measurements proved a local win:

  • vocab_size == 32000, temperature=None, PDL disabled, and rows in {16, 32, 64, 128, 512, 1024};
  • rows == 64, vocab_size == 32000, scalar temperature 1.0, and PDL enabled.

All other audited shapes remain on the prior dispatcher. The route observer exposes IDs 1..4; the 376-row audit has no external fallback.

Current GB300 evidence

The latest source-bound requalification used Slurm job 376374 on four GB300 GPUs (sm_103a). Timing is GPU-only CUPTI activity tracing with cold-L2 flushing and candidate/baseline blocks interleaved in the same process and GPU session.

Ratified 41-row performance set

All 41/41 declared performance rows passed correctness and their contract floor. Geometric-mean speedup is 2.099151447x and the worst row is 1.328849819x. Every candidate sample contains exactly one kernel and no row is within 2% of the floor.

Gate Baseline ms Candidate ms Baseline GB/s Candidate GB/s Speedup
b1, v64000, temperature=None 0.015200 0.005696 33.6842 89.8876 2.668539326x
b16, v256000, temperature=None 0.041536 0.014176 788.9060 2311.5124 2.930022573x
b1024, v128000, temperature=None 0.401473 0.277569 2611.8220 3777.7129 1.446274620x
b64, v32000, temperature=1.0, PDL 0.014592 0.006720 1122.8070 2438.0952 2.171428571x

The formal run used 100 ms warmup and 1000 ms arms with three same-session interleaved CUPTI pairs. Its aggregate evidence SHA-256 is 3a19cfdcc5686393c41a4213ac0fd5b358649f933b856b992813efd2ad91084e.

Supplemental full-376 timing map

Step 376374.331 measured the exact 376-row regression manifest across four GB300s (94 rows/GPU) with same-session interleaved CUPTI timing.

Metric Result
Correct rows measured 376/376
Rows with baseline_ms / candidate_ms >= 1.0 376/376
All-row geometric-mean speedup 1.704612604x
Worst per-row speedup 1.128205128x
Route coverage 1:76, 2:42, 3:251, 4:7
External fallback 0

Every row retained its raw baseline/candidate timing arrays, produced a fresh output, used a custom route, and launched exactly one candidate kernel. This sweep used 25 ms warmup, 100 ms measurement arms, and three interleaved pairs, so it is a supplemental broad performance map; the 41-row 100/1000 ms run remains the formal contract-duration performance gate. Aggregate artifact SHA-256: 7b7cf9fa28250b5651161efcbaa703f7d43276a9bb3a31f1d69924effbd3a0d6.

Correctness and implementation evidence

  • Original strict direct-vs-LogitsPipe CI node IDs: 9/9 passed. Full temperature/operator matrix: 54/54 passed. Independent ratified Torch oracle: 3/3 passed for rows 989 and vocab 111/32000/128256 with random per-row temperature, custom route, fresh output, and atol=rtol=1e-3.
  • Full relevant files at f0edac69: test_sampling.py 1834 passed / 121 skipped; test_logits_processor.py 591 passed / 12 skipped.
  • SASS step 376374.482: bootstrap vec8 retains LDG.256 / STG.256; route 4 retains 128-bit vec4 traffic; audited kernels contain no LDL / STL spill. SM103 retains the expected PDL ACQBULK / PREEXIT instructions.
  • NCU step 376374.485 (b64 route 4): 512 threads, 99 registers/thread, 23.68% active warps, 38.09% long-scoreboard stall, zero local loads/stores.
  • NCU step 376374.496 (b1024 bootstrap): 256 threads, 48 registers/thread, 47.55% active warps, 72.47% DRAM throughput, 86.59% long-scoreboard stall, zero local loads/stores.
  • Source-bound memcheck and synccheck both passed routes 1-4 with ERROR SUMMARY: 0 errors. Racecheck is not applicable because the final routes use no DSM/cluster execution.

tests/utils CI status

The refreshed internal pipeline was built from this exact head after a successful prepare retry.

  • H100 CUDA 12.9 and 13.0: both complete, 26 files succeeded / 0 failed.
  • RTX Pro 6000 Blackwell CUDA 12.9 and 13.0: both complete, 26 files succeeded / 0 failed. Each lane reports sampling 1809 passed / 146 skipped and logits_processor 591 passed / 12 skipped.
  • Native B300/GB200/GB300 jobs currently fail in runner preparation before checkout or pytest because the shared runner environment is not writable. One retry reproduced the same two-second infrastructure failure.
  • B300 multi-GPU jobs reach tests but fail an unrelated quantized_allreduce TCPStore port race: the test chooses an unreserved random port while concurrent mpirun instances run. A retry reproduced the same EADDRINUSE on a different port.
  • RTX 5090 CUDA 12.9 is still waiting for Slurm allocation 1765105 before checkout; CUDA 13.0 remains pending without a runner.

No completed job in this refreshed pipeline has a softmax/operator failure. The infrastructure-blocked and pending lanes are intentionally not represented as green.

Independent GB300 complete-denominator audit

A fresh allocation (377417) collected all 20,165 tests across the 25 tests/utils files. The two changed files are fully source-bound as reported above. The remaining 23-file sweep was followed by a source-bound replay of every failure using a non-editable 0.6.16 wheel built from exact head f0edac69; installed JIT-cache/cubin packages were blocked and all csrc/include/Jinja data came from the wheel. Two previously skipped IPC cases were rerun with four visible GB300s.

The effective complete-denominator accounting is 19,920 passed / 243 skipped / 2 non-softmax failed / 0 errors. The only failures are the independent TopK reference-index bug (torch.gather receives int32 instead of int64) and an FP4 logging replay that requires cuDNN backend >=91002 while the pinned image provides 91001. Softmax/kernel failures are 0. The receipt explicitly records that this is a combined complete-denominator audit, not a single-shot 20,165-case source-bound run. Receipt SHA-256: aad5289a20bf84045d531cc14800eb77054ed2edb443083021e747722eaa74f1.

Checklist

  • JIT and AOT registration cover the dispatcher and all four device translation units.
  • ABI, validation errors, temperature forms, PDL, route selection, and fresh-output ownership are covered.
  • 376/376 GB300 correctness and complete route coverage pass at the delivered device source.
  • 41/41 contract performance floors and all four main gates pass.
  • NCU, SASS, memcheck, and synccheck evidence is source-bound.
  • The strict LogitsPipe per-row-temperature regression is fixed at f0edac69.
  • External tests/utils is not fully green because several runners are blocked before source checkout and RTX 5090 is still pending.

Related to #4254

Summary by CodeRabbit

  • New Features

    • Added optimized softmax execution for supported Blackwell GPUs.
    • Supports scalar and per-row temperatures, including programmatic dependent launches where available.
    • Added automatic route selection with fallback behavior for unsupported configurations.
    • Integrated Blackwell softmax into AOT and JIT builds.
    • Updated fused temperature softmax operations to use the public softmax interface.
  • Bug Fixes

    • Added validation for CUDA inputs and temperature parameters.
    • Improved handling of negative-infinity inputs and non-aliasing outputs.
  • Tests

    • Added coverage for route selection, numerical behavior, temperature validation, fallback paths, and device-specific configurations.

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

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Blackwell-specific CUDA softmax kernels, deterministic route selection, PDL-aware launches, capability-gated JIT and API dispatch, fallback handling, input validation, and CUDA route and numerical tests.

Changes

Blackwell softmax

Layer / File(s) Summary
Kernel contracts and specialized variants
include/flashinfer/blackwell_softmax.cuh, csrc/blackwell_softmax_warp.cu, csrc/blackwell_softmax_rowwise.cu, csrc/blackwell_softmax_mr515_exp2.cu
Declares and implements warp, rowwise, and SM103 MR515 softmax kernels with scalar or per-row temperatures, approximate math, reductions, vectorized paths, and PDL controls.
Cooperative bootstrap execution
csrc/blackwell_softmax_bootstrap.cu
Adds unsplit and split softmax execution with partial maxima and sums, cooperative synchronization, and final normalization.
Route selection and CUDA launcher
csrc/blackwell_softmax.cu
Validates tensors, selects routes from dimensions, temperature mode, PDL, and compute capability, configures launches and workspace, exposes route queries, and applies fallback behavior.
JIT registration and API dispatch
flashinfer/jit/blackwell_softmax.py, flashinfer/aot.py, flashinfer/sampling.py, flashinfer/logits_processor/operators.py
Builds the Blackwell JIT module, registers custom operations, validates temperatures, detects SM100/SM103 support, and routes fused temperature softmax through the public API.
Validation and route coverage
tests/utils/test_sampling.py
Tests CPU rejection, temperature errors, route selection, output allocation, PDL combinations, and numerical results on SM100 and SM103.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant SoftmaxAPI
  participant blackwell_softmax
  participant BlackwellKernel
  participant OnlineSoftmax

  Caller->>SoftmaxAPI: request softmax
  SoftmaxAPI->>blackwell_softmax: dispatch supported Blackwell input
  blackwell_softmax->>BlackwellKernel: select and launch route
  BlackwellKernel-->>blackwell_softmax: write output
  blackwell_softmax-->>SoftmaxAPI: return status
  blackwell_softmax->>OnlineSoftmax: fallback for unsupported route
Loading

Possibly related PRs

Suggested labels: op: misc

Suggested reviewers: yzh119, aleozlx, dhiraj113

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.86% 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 and concisely identifies the optimized Blackwell softmax as the primary change.
Description check ✅ Passed The description covers the change, related issue, dispatch policy, validation results, test status, and checklist items; optional reviewer notes are not required.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (5)
flashinfer/sampling.py (1)

805-807: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Docstring is now out of sync with the dispatch behavior.

On SM100/SM103 this API routes to the Blackwell kernels, which ignore enable_pdl (see csrc/blackwell_softmax.cu:171-179), yet the docstring still promises automatic PDL for compute capability ≥ 9.0 and describes only the online-softmax kernel. Add a short note about the Blackwell route and the enable_pdl caveat.

As per coding guidelines, "Keep documentation synchronized with code changes, including infrastructure examples, skill files, conventions, deprecated approaches, and new error-handling patterns."

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

In `@flashinfer/sampling.py` around lines 805 - 807, Update the enable_pdl
parameter docstring in the sampling API to mention that SM100/SM103 dispatches
to Blackwell kernels, where enable_pdl is ignored, and clarify that the
automatic PDL behavior applies only to supported non-Blackwell online-softmax
kernels.

Source: Coding guidelines

tests/utils/test_sampling.py (2)

91-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the flashinfer.utils capability helper for the arch skip.

torch.cuda.get_device_capability() not in ((10, 0), (10, 3)) duplicates the gate that already lives in flashinfer/sampling.py:113-115 and skips the CUDA-version part of the check.

As per coding guidelines, "Skip tests on unsupported CUDA architectures using the appropriate flashinfer.utils checks or backend capability APIs, rather than running unsupported kernels." Based on learnings, flashinfer.utils.is_sm100a_supported(device) already covers SM103 (compute capability 10.3) in addition to SM100.

♻️ Proposed change
-    if torch.cuda.get_device_capability() not in ((10, 0), (10, 3)):
-        pytest.skip("Loom Softmax routes require SM100 or SM103")
+    device = torch.device("cuda:0")
+    if not is_sm100a_supported(device):
+        pytest.skip("Blackwell Softmax routes require SM100 or SM103")

Add the import alongside the existing test imports:

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

In `@tests/utils/test_sampling.py` around lines 91 - 93, Update
test_softmax_blackwell_routes to use flashinfer.utils.is_sm100a_supported for
the architecture and CUDA-version capability check, replacing the direct
torch.cuda.get_device_capability comparison. Add the helper import with the
existing test imports and preserve the current skip behavior for unsupported
devices.

Sources: Coding guidelines, Learnings


79-110: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider asserting the route and trimming the largest case.

Two optional improvements: (1) the test name promises route coverage but a silent cudaErrorNotSupported fallback to OnlineSoftmax would still pass every assertion — spying on get_blackwell_softmax_op (or asserting on a route counter) would make the dispatcher itself the thing under test; (2) (989, 128256) allocates ~1.5 GB of fp32 across logits, probs, and probs_ref plus torch.softmax temporaries, which is the one parametrization at real OOM risk on smaller SM103 parts.

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

In `@tests/utils/test_sampling.py` around lines 79 - 110, Strengthen
test_softmax_blackwell_routes by asserting that get_blackwell_softmax_op is
invoked, so unsupported-route fallback cannot satisfy the test. Reduce or remove
the (989, 128256, "per_row") parametrization while retaining representative
coverage of the intended Blackwell routes and avoiding excessive memory use.

Source: Coding guidelines

csrc/blackwell_softmax.cu (1)

44-64: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Record the provenance of these dispatch thresholds.

rows <= 128 && vocab_size <= 257, vocab_size >= 24576 && vocab_size <= 256000, measured_large_odd, etc. are sweep-derived constants with no note on how they were obtained or when they should be re-tuned. A one-line comment citing the benchmark/checkpoint (as the generated payload files do) makes future re-tuning safe.

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

In `@csrc/blackwell_softmax.cu` around lines 44 - 64, Add concise comments in
use_warp_kernel and use_rowwise_kernel documenting that the dispatch thresholds,
including the measured_large_odd range, were derived from the relevant
benchmark/checkpoint sweep and should be re-tuned when that data changes. Follow
the provenance-reference style used by generated payload files.
csrc/blackwell_softmax_warp.cu (1)

72-88: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the implicit 288-column capacity of this kernel.

The register tile (row_values[9] × 32 lanes) caps this kernel at 288 columns, but the enforcing predicate lives in csrc/blackwell_softmax.cu:44-48 (vocab_size <= 257). If that gate is ever widened, columns ≥288 are silently never written and the output buffer keeps uninitialized data rather than failing. A short comment next to the tile (and mirroring it in use_warp_kernel) makes the coupling explicit without touching the generated payload semantics.

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

In `@csrc/blackwell_softmax_warp.cu` around lines 72 - 88, The warp kernel’s
9-element-per-lane register tile supports only 288 columns, but this capacity is
implicit. Add concise comments beside row_values[9] and in use_warp_kernel
documenting the 288-column limit and its coupling to the existing vocab_size <=
257 gate, without changing generated payload behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@csrc/blackwell_softmax.cu`:
- Around line 171-179: Add a brief comment near the Blackwell fast-path
invocation explaining that enable_pdl is intentionally not applied there and
only affects the OnlineSoftmax fallback. Update the softmax documentation in
sampling.py to note this Blackwell-specific behavior, preserving the existing
fallback handling.
- Line 1: Run the repository clang-format hook on csrc/blackwell_softmax.cu and
include/flashinfer/blackwell_softmax.cuh, then commit the formatter’s changes:
update boolean-expression parenthesization in
use_warp_kernel/use_rowwise_kernel, wrap cudaLaunchCooperativeKernel arguments,
and align the kernel signature declarations in the extern "C" block.
- Around line 151-168: Validate inputs in the softmax entry point before
deriving pointers or launching kernels: when maybe_temperature_arr is present,
require a one-dimensional tensor with exactly logits.size(0) elements, matching
logits dtype and device; also require output to match logits shape, dtype, and
device. Reuse the existing CHECK_INPUT/CHECK_DIM validation conventions and
reject invalid inputs before any per-row parameter indexing.

In `@flashinfer/jit/blackwell_softmax.py`:
- Around line 22-34: Update the JIT specification returned by the blackwell
softmax setup to enable CUDA relocatable device code/device linking for the
cross-translation-unit kernel launch. Extend the existing extra CUDA flags in
the relevant function around gen_jit_spec and preserve the current source list
and compiler-version flags.

In `@flashinfer/sampling.py`:
- Around line 842-853: Update the Blackwell dispatch in the sampling path to
require logits.is_cuda before resolving a CUDA device index or calling
_supports_blackwell_softmax. Keep CPU tensors on the existing fallback path,
avoiding torch.cuda.current_device() and the Blackwell custom op for non-CUDA
inputs.

---

Nitpick comments:
In `@csrc/blackwell_softmax_warp.cu`:
- Around line 72-88: The warp kernel’s 9-element-per-lane register tile supports
only 288 columns, but this capacity is implicit. Add concise comments beside
row_values[9] and in use_warp_kernel documenting the 288-column limit and its
coupling to the existing vocab_size <= 257 gate, without changing generated
payload behavior.

In `@csrc/blackwell_softmax.cu`:
- Around line 44-64: Add concise comments in use_warp_kernel and
use_rowwise_kernel documenting that the dispatch thresholds, including the
measured_large_odd range, were derived from the relevant benchmark/checkpoint
sweep and should be re-tuned when that data changes. Follow the
provenance-reference style used by generated payload files.

In `@flashinfer/sampling.py`:
- Around line 805-807: Update the enable_pdl parameter docstring in the sampling
API to mention that SM100/SM103 dispatches to Blackwell kernels, where
enable_pdl is ignored, and clarify that the automatic PDL behavior applies only
to supported non-Blackwell online-softmax kernels.

In `@tests/utils/test_sampling.py`:
- Around line 91-93: Update test_softmax_blackwell_routes to use
flashinfer.utils.is_sm100a_supported for the architecture and CUDA-version
capability check, replacing the direct torch.cuda.get_device_capability
comparison. Add the helper import with the existing test imports and preserve
the current skip behavior for unsupported devices.
- Around line 79-110: Strengthen test_softmax_blackwell_routes by asserting that
get_blackwell_softmax_op is invoked, so unsupported-route fallback cannot
satisfy the test. Reduce or remove the (989, 128256, "per_row") parametrization
while retaining representative coverage of the intended Blackwell routes and
avoiding excessive memory use.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f0d159a-b8f5-4a76-8134-f89b16627dd3

📥 Commits

Reviewing files that changed from the base of the PR and between b1055a7 and bc6f334.

📒 Files selected for processing (9)
  • csrc/blackwell_softmax.cu
  • csrc/blackwell_softmax_bootstrap.cu
  • csrc/blackwell_softmax_rowwise.cu
  • csrc/blackwell_softmax_warp.cu
  • flashinfer/aot.py
  • flashinfer/jit/blackwell_softmax.py
  • flashinfer/sampling.py
  • include/flashinfer/blackwell_softmax.cuh
  • tests/utils/test_sampling.py

Comment thread csrc/blackwell_softmax.cu
Comment thread csrc/blackwell_softmax.cu
Comment thread csrc/blackwell_softmax.cu
Comment on lines +22 to +34
nvcc_flags = current_compilation_context.get_nvcc_flags_list(
supported_major_versions=[10]
)
return gen_jit_spec(
"blackwell_softmax",
[
jit_env.FLASHINFER_CSRC_DIR / "blackwell_softmax.cu",
jit_env.FLASHINFER_CSRC_DIR / "blackwell_softmax_bootstrap.cu",
jit_env.FLASHINFER_CSRC_DIR / "blackwell_softmax_rowwise.cu",
jit_env.FLASHINFER_CSRC_DIR / "blackwell_softmax_warp.cu",
],
extra_cuda_cflags=nvcc_flags,
)

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for an existing precedent of launching a __global__ defined in another TU, and for rdc/device-link flag support.
rg -n -C3 'cudaLaunchCooperativeKernel|reinterpret_cast<const void\*>\(kernel_' csrc | head -40
rg -n -C3 'rdc|dlink|relocatable-device-code' flashinfer/jit/core.py flashinfer/jit/compilation_context.py
ast-grep outline flashinfer/jit/core.py --match 'gen_jit_spec' --view expanded

Repository: flashinfer-ai/flashinfer

Length of output: 3729


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files around blackwell_softmax =="
git ls-files | rg '(^csrc/blackwell_softmax|flashinfer/aot\.py|flashinfer/jit/core\.py|flashinfer/jit/compilation_context\.py|csrc/mixed_comm\.cu|csrc/fmha_v2/templates/kernel\.jinja)' || true

echo
echo "== flashinfer/jit/core.py gen_jit_spec and nvcc flags =="
if [ -f flashinfer/jit/core.py ]; then
  nl -ba flashinfer/jit/core.py | sed -n '460,620p'
  echo
  rg -n -C4 'nvcc_flags|rdc|dlink|device-link|cuda|extra_cuda_cflags|_get_nvcc_flags' flashinfer/jit/core.py
fi

echo
echo "== compilation_context files =="
fd -i 'compilation_context|compiler|jit' flashinfer flashinfer/jit -t f
if [ -e flashinfer/jit/compilation_context.py ]; then
  rg -n -C4 'get_nvcc_flags_list|nvcc|rdc|dlink|device-link|cuda|extra_cuda_cflags' flashinfer/jit/compilation_context.py
fi

echo
echo "== AOT blackwell_softmax spec usage =="
if [ -f flashinfer/aot.py ]; then
  nl -ba flashinfer/aot.py | sed -n '620,655p'
  rg -n -C5 'blackwell_softmax|has_sm100|has_sm103|nvcc|rdc|dlink|device-link' flashinfer/aot.py
fi

echo
echo "== blackwell_softmax source outline =="
ast-grep outline csrc/blackwell_softmax.cuh --view expanded 2>/dev/null || true
ast-grep outline csrc/blackwell_softmax.cu --view expanded 2>/dev/null || true
nl -ba csrc/blackwell_softmax.cuh | sed -n '1,180p'
nl -ba csrc/blackwell_softmax.cu | sed -n '1,220p'

Repository: flashinfer-ai/flashinfer

Length of output: 526


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== flashinfer/jit/core.py gen_jit_spec and nvcc flags =="
sed -n '460,620p' flashinfer/jit/core.py
echo
grep -n -C4 'nvcc_flags\|rdc\|dlink\|device-link\|cuda\|extra_cuda_cflags\|_get_nvcc_flags' flashinfer/jit/core.py || true

echo
echo "== AOT blackwell_softmax spec usage =="
sed -n '620,655p' flashinfer/aot.py
grep -n -C5 'blackwell_softmax\|has_sm100\|has_sm103\|nvcc\|rdc\|dlink\|device-link' flashinfer/aot.py || true

echo
echo "== blackwell_softmax source snippets =="
sed -n '1,220p' csrc/blackwell_softmax.cuh 2>/dev/null || true
sed -n '1,220p' csrc/blackwell_softmax.cu 2>/dev/null || true

Repository: flashinfer-ai/flashinfer

Length of output: 31308


🌐 Web query:

CUDA device-linking -rdc=true separate compilation __global__ function address launch cross translation unit reinterpret_cast

💡 Result:

In CUDA, referencing the address of a global function or using it via reinterpret_cast across translation units requires the use of separate compilation and device linking (enabled via the -rdc=true compiler option) [1][2][3]. Without separate compilation (the default whole-program mode), each translation unit is compiled into a self-contained device program [1][3]. In this mode, attempting to resolve addresses or function pointers across file boundaries will result in incorrect behavior, invalid addresses, or linker errors, as the host-side stub functions and device-side kernels are not unified across object files [1][4]. Key technical constraints and mechanisms include: 1. Requirement of Relocatable Device Code (RDC): You must compile with -rdc=true to allow the NVCC device linker to resolve references to device symbols (kernels and device functions) across different object files [1][2]. 2. Limitations of Function Pointers: While you can take the address of a device or global function, doing so in host code returns a pointer that is only valid within the context of the CUDA runtime's internal mapping [4][5]. If you attempt to manipulate these addresses using reinterpret_cast or pass them across different compilation units without proper device-linking support, the CUDA runtime will fail to map the address to the correct device kernel, often leading to invalid device function errors or undefined behavior [4][6][5]. 3. Kernel Launch Stubs: When you launch a global function, you are actually calling a host-side stub function generated by the compiler [1][4]. This stub is responsible for interacting with the CUDA runtime to launch the correct device entry point [1]. If multiple translation units define kernels or use shared logic, the host linker may conflict if symbols are not properly managed or if the RDC mode is not consistently applied, leading to scenarios where the wrong kernel or no kernel is found at the expected address [1][4]. 4. Best Practices: To use function pointers reliably across files, define the function pointer in device code (e.g., as a device variable) and use cudaMemcpyFromSymbol to retrieve its address on the host for setup, or maintain both the caller and the target function within the same linkable module (e.g., the same static library) [7][8][9]. Using shared libraries (.so/.dll) for cross-translation-unit device code is significantly more restrictive and generally only works effectively with static linking [8]. Top results: [1], [2], [4], [8]

Citations:


Enable device linking for the cross-TU kernel launch.

blackwell_softmax.cu launches kernels defined in other translation units via reinterpret_cast<const void*>(...), which requires CUDA RDC/device linking (-rdc=true). Add the device-link flags through this JIT spec instead of compiling each .cu as an isolated object.

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

In `@flashinfer/jit/blackwell_softmax.py` around lines 22 - 34, Update the JIT
specification returned by the blackwell softmax setup to enable CUDA relocatable
device code/device linking for the cross-translation-unit kernel launch. Extend
the existing extra CUDA flags in the relevant function around gen_jit_spec and
preserve the current source list and compiler-version flags.

Comment thread flashinfer/sampling.py Outdated
@yyihuang
yyihuang marked this pull request as draft August 3, 2026 21:03
@yyihuang
yyihuang marked this pull request as ready for review August 5, 2026 22:52

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
csrc/blackwell_softmax_mr515_exp2.cu (1)

86-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the overloaded temperature parameter, or pass an explicit PDL flag.

This kernel derives enable_pdl from temperature == nullptr. The three sibling kernels use bit 2 of parameter_kind. The polarity is correct today: the launcher passes nullptr exactly when launch_with_pdl is true, and TEMP_KIND is 0 so the kernel never dereferences temperature.

The coupling is fragile. If the mr515 route later reads temperature, or if a caller passes a null temperature array for any other reason, PDL behavior changes silently. The declaration in include/flashinfer/blackwell_softmax.cuh at Lines 49-50 gives no indication that the second parameter carries a control flag.

Add a comment at this site that states the encoding and its dependency on TEMP_KIND == 0. Also add a matching note to the header declaration. The project guidelines require documented rationale for special choices on kernel hot paths.

Suggested comment
 `#if` defined(__CUDA_ARCH__) && __CUDA_ARCH__ == 1030
+    // Integration-only PDL encoding. TEMP_KIND==0 never dereferences the
+    // `temperature` slot, so the launcher passes nullptr to request the
+    // wait/signal pair. Revisit if this specialization ever reads temperature.
     const bool enable_pdl = temperature == nullptr;
     if (enable_pdl) {
         asm volatile("griddepcontrol.wait;" ::: "memory");
     }
 `#endif`
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@csrc/blackwell_softmax_mr515_exp2.cu` around lines 86 - 91, Document the
overloaded temperature parameter at the __CUDA_ARCH__ == 1030 enable_pdl logic,
stating that temperature == nullptr encodes PDL enabled and is valid only
because TEMP_KIND == 0 means the kernel does not dereference temperature. Add
the same rationale to the corresponding declaration in the blackwell_softmax
header, without changing the existing behavior.

Source: Coding guidelines

csrc/blackwell_softmax_bootstrap.cu (1)

375-386: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Record why splitting the 256-bit store is safe here.

The fallback replaces one st.global.v8.b32 with two st.global.v4.b32. For loads that substitution is unconditionally equivalent. For stores it is equivalent only when no concurrent reader can observe the 32-byte region in a partially written state.

The current code satisfies that condition. The Line 375 stores run only in the splits == 1 branch, which has no cross-block reader of output. The Line 541 stores run after cooperative_groups::this_grid().sync() at Line 415, and no later code in the kernel reads output.

Add a short comment at these two sites that states the assumption. A future change that reads output from another block while the grid runs would make the split store observable as torn, and the provenance note at Lines 19-21 does not capture that.

Also applies to: 541-552

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

In `@csrc/blackwell_softmax_bootstrap.cu` around lines 375 - 386, Add concise
comments at both the fallback store sites around the split v4 stores (the blocks
near the current 375 and 541 locations) documenting that splitting the 256-bit
store is safe because no concurrent block can read output: the first is in the
splits == 1 path, and the second follows grid synchronization with no later
output reads. Note that introducing concurrent readers would make the split
observable as torn.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@csrc/blackwell_softmax_bootstrap.cu`:
- Around line 87-93: Strip the PDL ABI bit before any parameter_kind checks in
the adapted kernels: in csrc/blackwell_softmax_bootstrap.cu lines 87-93,
csrc/blackwell_softmax_warp.cu lines 67-73, and
csrc/blackwell_softmax_rowwise.cu lines 85-91, move enable_pdl extraction and
parameter_kind masking outside and before the __CUDA_ARCH__ == 1030 guard. Keep
only the griddepcontrol.wait assembly behind the architecture guard.

In `@csrc/blackwell_softmax_mr515_exp2.cu`:
- Line 197: Align the non-mr515 softmax routes selected by
select_softmax_route—kWarp, kRowwise, and kBootstrap—with the mr515 zero-sum
behavior by guarding reciprocal normalization when the row sum is zero and
returning zero probabilities instead of multiplying by infinity. Alternatively,
add coverage that exercises all-−inf rows through each route and verifies 0.0f
outputs.

In `@csrc/blackwell_softmax.cu`:
- Around line 303-321: Update query_softmax_route to return
SoftmaxRoute::kFallback when the current device capability is not supported by
_supports_blackwell_softmax, before invoking select_softmax_route. Keep the
existing capability queries and route selection for allowed SM100/SM103 devices,
and use the queried device_major/device_minor values for the gate.

---

Nitpick comments:
In `@csrc/blackwell_softmax_bootstrap.cu`:
- Around line 375-386: Add concise comments at both the fallback store sites
around the split v4 stores (the blocks near the current 375 and 541 locations)
documenting that splitting the 256-bit store is safe because no concurrent block
can read output: the first is in the splits == 1 path, and the second follows
grid synchronization with no later output reads. Note that introducing
concurrent readers would make the split observable as torn.

In `@csrc/blackwell_softmax_mr515_exp2.cu`:
- Around line 86-91: Document the overloaded temperature parameter at the
__CUDA_ARCH__ == 1030 enable_pdl logic, stating that temperature == nullptr
encodes PDL enabled and is valid only because TEMP_KIND == 0 means the kernel
does not dereference temperature. Add the same rationale to the corresponding
declaration in the blackwell_softmax header, without changing the existing
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 348ec8e8-f10c-4c47-ae66-c77954ee58cb

📥 Commits

Reviewing files that changed from the base of the PR and between 754aee2 and 947d9c8.

📒 Files selected for processing (9)
  • csrc/blackwell_softmax.cu
  • csrc/blackwell_softmax_bootstrap.cu
  • csrc/blackwell_softmax_mr515_exp2.cu
  • csrc/blackwell_softmax_rowwise.cu
  • csrc/blackwell_softmax_warp.cu
  • flashinfer/jit/blackwell_softmax.py
  • flashinfer/sampling.py
  • include/flashinfer/blackwell_softmax.cuh
  • tests/utils/test_sampling.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • flashinfer/jit/blackwell_softmax.py

Comment on lines +87 to +93
#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ == 1030
const bool enable_pdl = (parameter_kind & 4) != 0;
parameter_kind &= 3;
if (enable_pdl) {
asm volatile("griddepcontrol.wait;" ::: "memory");
}
#endif

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check whether an sm_100a Blackwell softmax binary can run on an sm_103 device.
set -uo pipefail

echo "=== Blackwell softmax JIT module: arch and gencode flags ==="
fd -t f 'blackwell_softmax.py' --exec rg -n -C 8 'sm_10|103|100|arch|gencode|nvcc|flags|capability'

echo "=== AOT build registration ==="
fd -t f 'aot.py' --exec rg -n -C 8 'blackwell_softmax'

echo "=== is_sm103 producer and consumers ==="
rg -nP --type=cu --type=cpp --type=py -C 6 '\bis_sm103\b'

echo "=== Python capability gate for the softmax dispatch ==="
fd -t f 'sampling.py' --exec rg -n -C 10 'blackwell|get_device_capability|major|minor'

echo "=== Repository conventions for sm_103 arch selection ==="
rg -nP -C 4 "sm_103a?|'103'|\"103\"|\b103\b" --type=py -g '!**/tests/**' | head -80

Repository: flashinfer-ai/flashinfer

Length of output: 12995


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Files and top-level build context ==="
git ls-files | rg '(^|/)env\.py$|(^|/)blackwell_softmax\.py$|(^|/)sampling\.py$|(^|/)aot\.py$|(^|/)blackwell_softmax.*\.cu$|(^|/)build' | head -200

echo
echo "=== blackwell_softmax related snippets ==="
for f in \
  python/flashinfer/jit/blackwell_softmax.py \
  python/flashinfer/sampling.py \
  csrc/blackwell_softmax.cu \
  csrc/blackwell_softmax_bootstrap.cu \
  csrc/blackwell_softmax_warp.cu \
  csrc/blackwell_softmax_rowwise.cu \
  csrc/blackwell_softmax_mr515_exp2.cu
do
  [ -f "$f" ] || continue
  echo "---- $f ----"
  sed -n '1,230p' "$f"
done

echo
echo "=== Search exact PDL/flag patterns in cu/cpp ==="
rg -n --glob '*.cu' --glob '*.cpp' --glob '*.cc' --glob '*.cuh' -C 5 'parameter_kind|max_temperature|scalar_temperature|enable_pdl|griddepcontrol.wait|is_sm103|__CUDA_ARCH__|1030|1000' .

Repository: flashinfer-ai/flashinfer

Length of output: 50381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Python JIT env and compilation context ==="
fd -t f 'env.py|context.py|compilation.*\.py' -x sh -c 'echo "--- $1"; rg -n -C 6 "CUDA|CUDAARCH|__cuda|nvcc_flags|supported_major_versions|sm_10|capability|arch" "$1"' sh {}

echo
echo "=== Blackwell softmax C++ entrypoints ==="
for f in \
  csrc/blackwell_softmax.cu \
  csrc/blackwell_softmax_bootstrap.cu \
  csrc/blackwell_softmax_warp.cu \
  csrc/blackwell_softmax_rowwise.cu
do
  echo "---- $f ----"
  wc -l "$f"
  rg -n -C 8 'softmax_dispatch|parameter_kind|scalar_temperature|max_temperature|template|__device__|softmax_route|parameter_kind' "$f" | head -260
done

echo
echo "=== Narrow cu search for relevant symbols ==="
rg -n --glob '*.cu' '(__CUDA_ARCH__|1030|1000|parameter_kind|scalar_temperature|max_temperature|is_sm103)' csrc include python -g '!**/nv_internal/**' -g '!**/test**/**' | sed -n '1,220p'

Repository: flashinfer-ai/flashinfer

Length of output: 49311


Strip the PDL ABI bit unconditionally before reading parameter_kind.

parameter_kind_i is passed with bit 2 on when is_sm103 && enable_pdl, but each adapted kernel strips it only while compiling for __.cuda_arch__ == 1030. Keep the griddepcontrol.wait path behind the arch guard and move const bool enable_pdl plus parameter_kind &= 3 above the guard at blackwell_softmax_bootstrap.cu#87, blackwell_softmax_warp.cu#67, and blackwell_softmax_rowwise.cu#85. Otherwise parameter_kind == 2 checks will fail and per-row temperatures will use scalar_temperature.

📍 Affects 3 files
  • csrc/blackwell_softmax_bootstrap.cu#L87-L93 (this comment)
  • csrc/blackwell_softmax_warp.cu#L67-L73
  • csrc/blackwell_softmax_rowwise.cu#L85-L91
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@csrc/blackwell_softmax_bootstrap.cu` around lines 87 - 93, Strip the PDL ABI
bit before any parameter_kind checks in the adapted kernels: in
csrc/blackwell_softmax_bootstrap.cu lines 87-93, csrc/blackwell_softmax_warp.cu
lines 67-73, and csrc/blackwell_softmax_rowwise.cu lines 85-91, move enable_pdl
extraction and parameter_kind masking outside and before the __CUDA_ARCH__ ==
1030 guard. Keep only the griddepcontrol.wait assembly behind the architecture
guard.

Comment thread csrc/blackwell_softmax_mr515_exp2.cu
Comment thread csrc/blackwell_softmax.cu
@yyihuang

yyihuang commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run tests/utils

@yyihuang yyihuang closed this Aug 6, 2026
@yyihuang
yyihuang deleted the perf/blackwell-softmax-loom branch August 6, 2026 01:06
@yyihuang
yyihuang restored the perf/blackwell-softmax-loom branch August 6, 2026 01:19
@yyihuang yyihuang reopened this Aug 6, 2026
@yyihuang

yyihuang commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run tests/utils

@yyihuang
yyihuang marked this pull request as draft August 6, 2026 04:15
@yyihuang

yyihuang commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run tests/utils

@yyihuang

yyihuang commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Update on the refreshed tests/utils run for head f0edac69:

The bot's earlier “0/0 executed test jobs” comment was generated by the report job before the long GPU jobs finished, so it is now stale. The completed jobs show:

  • H100 CUDA 12.9 and 13.0: 26 files succeeded, 0 failed on each.
  • RTX Pro 6000 Blackwell CUDA 12.9 and 13.0: 26 files succeeded, 0 failed on each. Each has sampling 1809 passed / 146 skipped and logits_processor 591 passed / 12 skipped.
  • Native B300/GB200/GB300 jobs fail before checkout/pytest because their shared runner workspace cannot remove a stale .tmp/gitlab_runner_env; a retry reproduced the same infrastructure error.
  • B300 multi-GPU reaches tests but hits an unrelated unreserved TCPStore port collision in test_quantized_allreduce.py; a retry reproduced EADDRINUSE on a different port.
  • RTX 5090 CUDA 12.9 is still waiting for its Slurm allocation; CUDA 13.0 remains pending without a runner.

I also ran an independent four-GB300 complete-denominator audit. It accounts for all 20,165 tests/utils cases as 19,920 passed / 243 skipped / 2 non-softmax failed / 0 errors, with 0 softmax/kernel failures. The only failures are (1) a TopK reference test passing an int32 index to torch.gather, which requires int64, and (2) an FP4 logging replay requiring cuDNN backend >=91002 while the pinned image supplies 91001. A source wheel built from exact f0edac69 replayed the initial ABI-mismatch failures as 855/856 passed; four-GPU IPC passed 2/2. Receipt SHA-256: aad5289a20bf84045d531cc14800eb77054ed2edb443083021e747722eaa74f1.

The strict LogitsPipe regression remains green (original node IDs 9/9, full temperature matrix 54/54, independent Torch oracle 3/3), and the full GB300 kernel contract remains 376/376 correctness plus 41/41 performance floors.

@yyihuang
yyihuang marked this pull request as ready for review August 6, 2026 09:51
@yyihuang

yyihuang commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run tests/utils

@flashinfer-ai flashinfer-ai deleted a comment from flashinfer-bot Aug 18, 2026
@flashinfer-ai flashinfer-ai deleted a comment from flashinfer-bot Aug 18, 2026
@flashinfer-ai flashinfer-ai deleted a comment from flashinfer-bot Aug 18, 2026
@flashinfer-ai flashinfer-ai deleted a comment from flashinfer-bot Aug 18, 2026
@flashinfer-ai flashinfer-ai deleted a comment from flashinfer-bot Aug 18, 2026
@flashinfer-ai flashinfer-ai deleted a comment from flashinfer-bot Aug 18, 2026
@flashinfer-ai flashinfer-ai deleted a comment from flashinfer-bot Aug 18, 2026
@kahyunnam kahyunnam added the op: misc norm, activation, sampling, RoPE, quantization, etc. label Sep 2, 2026
@flashinfer-bot

Copy link
Copy Markdown
Collaborator

[FAILED] Pipeline #61373025 — 16/18 executed test jobs passed

No usable JUnit artifact was available; individual tests and nightly comparison could not be recovered.

Unit Tests

GPU CUDA 12.9 CUDA 13.0 Notes
5090 ✅ Pass ❔ Unknown Unknown: script failed before producing a JUnit report (1 job; CUDA 13.0)
B300 ✅ Pass ✅ Pass
GB200 ✅ Pass ✅ Pass
GB300 ✅ Pass ✅ Pass
H100 ✅ Pass ✅ Pass
RTX Pro 6000 Blackwell ✅ Pass ✅ Pass

✅ Pass · 🟡 Old failure · ❌ New failure · ⏱ Test timeout · ⚠️ Infrastructure · ❔ Unknown or unclassified · — Not run

Multi-GPU and Multi-Node Tests — 5/6 passed

GPU CUDA 12.9 CUDA 13.0 Notes
B300 (multi-GPU) ✅ Pass ❔ Unknown Unknown: script failed before producing a JUnit report (1 job; CUDA 13.0)
GB200 (multi-node) ✅ Pass ✅ Pass
GB300 (multi-node) ✅ Pass ✅ Pass
Failure details

Timeouts, infrastructure, or incomplete jobs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

op: misc norm, activation, sampling, RoPE, quantization, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants