Skip to content

feat(topk): support compact page table transforms - #4315

Merged
bkryu merged 14 commits into
flashinfer-ai:mainfrom
zianglih:agent/dsv4-compact-page-topk
Aug 9, 2026
Merged

bkryu merged 14 commits into
flashinfer-ai:mainfrom
zianglih:agent/dsv4-compact-page-topk

Conversation

@zianglih

@zianglih zianglih commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

📌 Description

@HumansAnd

SGLang's DeepSeek V4 indexer uses a compact page table in which one entry represents 64 score positions. Its score rows may also have padding between rows, and CUDA graph capture owns the translated and raw-index output buffers. The current top_k_page_table_transform contract assumes one page-table entry per score and allocates only the translated output, so SGLang has to split this into top_k, score re-gathering, compact-page translation, and output copies.

This PR extends the existing fused page-table transform for that layout:

  • Add page_size, defaulting to 1 for backward compatibility.
  • Add optional caller-owned out and out_raw_indices buffers. Raw indices remain positionally aligned with translated indices, including deterministic post-sort and -1 padding; the two buffers must be disjoint.
  • Honor the input row stride instead of requiring tightly packed rows, while preserving last-dimension contiguity and alignment-safe vectorization.
  • Propagate the contract through the Python API, TVM FFI binding, Radix and Filtered implementations, graph-safe dispatch, deterministic post-sort, and the trivial length <= k path.
  • Extend only the operation-local trace template for page_size. Its core one-output definition excludes destination buffers; raw-output calls are deliberately not emitted and use a distinct routing identity so Trace Apply falls back to the API.

For each selected local score index idx, the compact transform is:

physical_page = src_page_table[
    batch_idx, page_table_row_start + idx // page_size
]
output = physical_page * page_size + idx % page_size

page_table_row_starts is measured in page-table entries, while row_starts is measured in score elements. When page_size > 1 and row_starts is supplied, callers must therefore provide page_table_row_starts explicitly rather than relying on the existing shared-start behavior.

The C++ path uses policy types rather than a second boolean mode. The FFI boundary selects DirectPageTableKernelPolicy or ConfigurablePageTableKernelPolicy once, and the kernel ABI is derived structurally from the policy type: an empty policy contributes zero arguments, while a stateful trivially-copyable policy contributes one object. Host dispatch carries one typed policy value through the selection stack; only the terminal launch forms the zero-or-one argument pack. The direct policy therefore adds no kernel argument or device branch, while the configurable policy owns score-row layout, logical-to-physical translation, and the optional raw-index sink. Future transforms that share the flat row-selection contract can extend the configurable policy without multiplying kernel variants or changing the selection kernels. Direct translation sites retain their original expressions so supported-path SASS is preserved exactly.

This is a clean extension of the API introduced in #4169: page_size=1, omitted output buffers, and tightly packed inputs retain the existing behavior and cluster fast path. SGLang #33237 uses this API to replace its DeepSeek V4 unfused workaround with one graph-safe FlashInfer call.

⚡ Performance

Fresh performance validation compares the exact rebase base 29196cf437778906c72630dc5d9850de547501de with head cf0319a3497e252219544c0a8b4168c6ba598f88 on the same NVIDIA B200 (driver 580.126.09), CUDA 13.2.78, and PyTorch 2.13.0+cu132. Base and head used separate editable source trees and JIT workspaces. benchmarks/bench_topk.py is byte-identical on both sides (SHA-256 57cd1ca61b38120380cb9ea7cf81ae3ee972484724bb2bb785649ae05cc199d9).

The script uses CUPTI (cupti-python 13.2.0 and nvidia-cuda-cupti 13.2.75), 10 dry runs, 100 measured iterations, cold L2, and the median. After #4295 it already sets use_cuda_graph=False, so no temporary benchmark-source edit was needed and the CUPTI plus CUDA graph instability is excluded.

Exact PR-body commands:

python3 benchmarks/bench_topk.py \
  --op dsa_topk --dtype bf16 --dsa-input-pattern dsa_relu \
  --dsa-case all --dsa-topk 2048 --tie-break

python3 benchmarks/bench_topk.py \
  --op varlen --dtype bf16 --length-dist causal \
  --varlen-k 2048 --varlen-q-len 128 --tie-break

After #4295 these exact commands keep deterministic=False: they measure the default nondeterministic path plus SMALL/LARGE tie selection without the canonical output-order sort. For fair DSA pairing, the confirmation runs used the same command after torch.manual_seed(1234) and torch.cuda.manual_seed_all(1234). Current-mode DSA ran base/head/head/base; varlen used base/head/head/base and its built-in per-case seeds. Canonical-output coverage repeated both commands with --deterministic on base and head. All comparisons use matched per-case medians; negative PR delta means the head is faster.

workload / mode default PR delta (worst) deterministic PR delta (worst) tie-small PR delta (worst) tie-large PR delta (worst)
DSA, current ABBA +0.05% (+0.13%) n/a -0.09% (+0.08%) -0.01% (+0.12%)
page-table varlen, current ABBA +0.01% (+0.10%) n/a +0.06% (+0.19%) +0.05% (+0.24%)
ragged varlen, current ABBA +0.02% (+0.08%) n/a +0.01% (+0.13%) -0.02% (+0.14%)
DSA, explicit deterministic +0.03% (+0.15%) -0.09% (+0.10%) +0.01% (+0.41%) -0.09% (no regressed point)
page-table varlen, explicit deterministic +0.01% (+0.10%) -0.18% (+0.03%) -0.09% (no regressed point) -0.05% (+0.09%)
ragged varlen, explicit deterministic -0.01% (+0.06%) -0.10% (+0.01%) -0.01% (+0.03%) -0.13% (+0.08%)

All 12 varlen rows had identical len_min, len_mean, len_max, and triv% across paired runs. Current-mode suite geomeans are within +0.06%, the largest individual delta is +0.24%, and deterministic geomeans are flat or faster apart from a +0.01% DSA tie-small geomean. This supports no measurable kernel-latency regression after the final rebase.

These sweeps exercise the existing direct compatibility paths (page_size=1, contiguous rows, no caller-owned outputs). The configured V4 path has no pre-PR API equivalent. Its policy cleanup was separately checked by an eager ABBA host audit using page_size=64, padded row stride, raw output, and all three production shapes: its configured 12-case batched end-to-end geomean was +0.053%, with a worst point of +0.380%.

The policy design also has complementary binary evidence from the exhaustive audit performed after #4295:

direct: 354/354 affected kernels, 0 normalized SASS/resource/KPARAM mismatches
  radix=180, filtered=132, finalizer=42
configured: 116 kernels
  radix=60, filtered=44, finalizer=12
  exactly one trailing 24-byte policy object; PageTable mode only

The direct variants retained their upstream parameter counts and constant-bank spans. The configured variants add one policy object without a second policy family or boolean template axis.

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

Validation used an editable source build on one NVIDIA B200 with CUDA 13.2.78 and PyTorch 2.13.0+cu132. The rebased head was synced to an isolated source tree and JIT workspace on flashinfer-pr4366-cu132:

pre-commit run --all-files
Passed

python3 -m pytest -q \
  tests/utils/test_topk.py::test_top_k_page_table_transform_misaligned_scores_without_row_starts \
  tests/utils/test_topk.py::test_top_k_page_table_transform_compact_pages_cuda_graph_replay
18 passed, 3 warnings in 93.98s

python3 -m pytest -q \
  tests/topk_varlen/test_topk_varlen.py::test_radix_preallocated_outputs \
  tests/topk_varlen/test_topk_varlen.py::test_out_values_ignored_when_return_values_false
6 passed, 39 warnings in 4.41s

python3 -m pytest -q \
  tests/trace/test_fi_trace_template_consistency.py \
  tests/trace/test_template_init.py \
  -k top_k_page_table_transform
6 passed, 1 skipped, 972 deselected, 3 warnings in 0.27s

python3 -m pytest -q tests/utils/test_topk.py
1495 passed, 3 warnings in 7.56s

Final CUDA 13.2 validation was rerun on rebased head cf0319a3497e252219544c0a8b4168c6ba598f88. It covers the page-table changes plus the optional-output overlap from #3901 after conflict resolution. git range-diff, git diff --check, and pre-commit run --all-files also passed.

The Top-K matrix covers Radix multi-CTA and Filtered dispatch, graph-safe mode, deterministic mode, optional raw output for default and compact page sizes, independent score/page-table starts, compact and default page sizes, padded row strides, misaligned input bases, trivial and selected rows, and CUDA graph replay with mutated inputs. The trace checks cover the operation-local schema and default-argument initialization. A separate smoke check also verified that the Python page_size <= 2**30 validation matches the native contract.

The warnings are existing CUTLASS DSL deprecations from tests/conftest.py and flashinfer/cute_dsl/utils.py.

Reviewer Notes

Review focus is welcome on the positional pairing of raw and translated outputs across deterministic post-sort, the structural zero-or-one policy ABI, and the page-table transform in the Radix multi-CTA and graph-safe Filtered epilogues.

Summary by CodeRabbit

  • New Features
    • Added support for compact page tables with configurable page sizes.
    • Added optional raw-index outputs alongside translated physical indices.
    • Added reusable output buffers for flexible result storage.
    • Improved support for empty rows, padding, non-contiguous inputs, and physical-page remapping.
  • Bug Fixes
    • Added validation for page metadata and output compatibility.
    • Improved CUDA graph and algorithm-path support.
  • Documentation
    • Updated API and tracing documentation for page sizes, physical indices, and raw-index outputs.

@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 Aug 2, 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

The page-table top-k transform now supports compact pages, physical-slot translation, optional raw-index outputs, reusable buffers, expanded validation, policy-based CUDA dispatch, trace support, and broader regression coverage.

Changes

Paged top-k transformation

Layer / File(s) Summary
API contract and validation
csrc/flashinfer_topk_binding.cu, csrc/topk.cu, flashinfer/topk.py
The API accepts page_size, reusable output buffers, and raw-index buffers. Validation covers metadata, layouts, strides, page sizes, row starts, and buffer overlap.
CUDA policy and index translation
include/flashinfer/topk.cuh, csrc/topk.cu
Direct and configurable policies propagate input strides, page bits, raw-index storage, and physical-index translation through radix, filtered, sorting, and finalization paths.
Trace reference and dispatch
flashinfer/trace/templates/sampling.py
Trace reference logic translates compact-page indices into physical slots. Trace templates expose optional page-size metadata and exclude raw-index side-output requests.
Reference and regression coverage
tests/utils/test_topk.py
Tests cover compact pages, physical remapping, raw outputs, buffer reuse, non-contiguous and misaligned inputs, zero-length rows, CUDA graph replay, row starts, tie-breaks, and deterministic ordering.

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

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant PythonWrapper
  participant TopKPageTableTransformDispatch
  participant CUDAKernel
  participant OutputBuffers
  Caller->>PythonWrapper: page_size and optional buffers
  PythonWrapper->>TopKPageTableTransformDispatch: validated transform request
  TopKPageTableTransformDispatch->>CUDAKernel: policy, stride, and page bits
  CUDAKernel->>OutputBuffers: physical indices and optional raw indices
  OutputBuffers->>Caller: transformed results
Loading

Possibly related PRs

Suggested labels: op: attention, model: dsv4

Suggested reviewers: aleozlx, saltyminty, yzh119

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% 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 main change: support for compact page table transforms.
Description check ✅ Passed The description covers the change, related issues, testing, performance validation, checklist, and reviewer focus in substantial detail.
✨ 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

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 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: 1

🧹 Nitpick comments (3)
tests/utils/test_topk.py (2)

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

Remove the no-op statement.

scores.size(1) has no effect. It is a leftover of a removed local variable.

♻️ Proposed cleanup
     num_rows = scores.size(0)
-    scores.size(1)
     device = scores.device
🤖 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_topk.py` at line 424, Remove the standalone no-op
scores.size(1) statement from the affected test, leaving the surrounding test
logic unchanged.

791-803: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a raw-output case for page_size=1.

The matrix exercises out_raw_indices only with page_size=64. The page_size=1 layout is the default and legacy path, and it stays untested for raw indices. Add one case to cover it.

💚 Proposed parameter addition
         pytest.param(1, False, False, id="page_size_1_shared_starts"),
         pytest.param(1, True, False, id="page_size_1_separate_starts"),
+        pytest.param(1, True, True, id="page_size_1_raw_output"),
         pytest.param(64, True, False, id="compact_pages"),
         pytest.param(64, True, True, id="compact_pages_raw_output"),

Note: the raw-index assertions at lines 954-977 read page_table_row_starts, which is set for separate_page_table_row_starts=True. The proposed case keeps that assertion valid.

🤖 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_topk.py` around lines 791 - 803, Add a parametrized test
case in the matrix for page_size=1 with separate_page_table_row_starts=True and
with_raw_output=True, ensuring the existing raw-index assertions over
page_table_row_starts remain valid.
csrc/topk.cu (1)

125-129: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add an overlap check between output_page_table and maybe_output_raw_indices.

The Python wrapper rejects overlapping buffers, but this binding is also reachable from other FFI callers. The kernel writes both buffers, so an overlap corrupts results silently. The buffers are contiguous and have equal shape, so a pointer-range check is cheap.

♻️ Proposed validation
   if (maybe_output_raw_indices.has_value()) {
     CHECK_INPUT_AND_TYPE(maybe_output_raw_indices.value(), dl_int32);
     CHECK_DIM(2, maybe_output_raw_indices.value());
     CHECK_SHAPE(maybe_output_raw_indices.value(), output_page_table);
+    const char* out_ptr = static_cast<const char*>(output_page_table.data_ptr());
+    const char* raw_ptr =
+        static_cast<const char*>(maybe_output_raw_indices.value().data_ptr());
+    const size_t out_bytes = static_cast<size_t>(output_page_table.numel()) * sizeof(int32_t);
+    TVM_FFI_ICHECK(out_ptr + out_bytes <= raw_ptr || raw_ptr + out_bytes <= out_ptr)
+        << "output_page_table and output_raw_indices must not overlap";
   }
🤖 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/topk.cu` around lines 125 - 129, Add a pointer-range overlap validation
in the maybe_output_raw_indices validation block, comparing its contiguous
storage range with output_page_table before the kernel runs. Reject any
overlapping buffers while preserving the existing type, dimension, and shape
checks.
🤖 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 `@flashinfer/trace/templates/sampling.py`:
- Around line 1108-1114: Update the trace setup for the axes mapping near
`top_k_page_table_transform` so the trace namespace explicitly provides the
default `page_size` value, allowing the `Const(abbrev="ps")` axis to produce
`ps1` in definition names. Regenerate the required test JSON using
`top_k_page_table_transform.init(...)`.

---

Nitpick comments:
In `@csrc/topk.cu`:
- Around line 125-129: Add a pointer-range overlap validation in the
maybe_output_raw_indices validation block, comparing its contiguous storage
range with output_page_table before the kernel runs. Reject any overlapping
buffers while preserving the existing type, dimension, and shape checks.

In `@tests/utils/test_topk.py`:
- Line 424: Remove the standalone no-op scores.size(1) statement from the
affected test, leaving the surrounding test logic unchanged.
- Around line 791-803: Add a parametrized test case in the matrix for
page_size=1 with separate_page_table_row_starts=True and with_raw_output=True,
ensuring the existing raw-index assertions over page_table_row_starts remain
valid.
🪄 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: 3c7c13b4-9a10-44a2-bfb8-ed6fdc040771

📥 Commits

Reviewing files that changed from the base of the PR and between d020372 and 3c9627782a2345fddcae4aa51bb7de00ece837c0.

📒 Files selected for processing (8)
  • csrc/flashinfer_topk_binding.cu
  • csrc/topk.cu
  • flashinfer/topk.py
  • flashinfer/trace/template.py
  • flashinfer/trace/templates/sampling.py
  • include/flashinfer/topk.cuh
  • tests/trace/test_fi_trace_template_consistency.py
  • tests/utils/test_topk.py

Comment thread flashinfer/trace/templates/sampling.py

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
csrc/topk.cu (1)

92-137: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Require explicit layout contracts before passing raw pointers.

CHECK_INPUT_AND_TYPE and CHECK_LAST_DIM_CONTIGUOUS_INPUT already require CUDA tensors with contiguous-layout semantics, but this code still passes output_page_table, lengths, row_to_batch, row_starts, page_table_row_starts, and output_raw_indices as raw pointers. Assert the expected strided layouts as part of the binding contract, or pass strides to the dispatch path, to prevent a direct FFI caller from using tensors with the accepted shapes but incorrect underlying storage.

🤖 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/topk.cu` around lines 92 - 137, Require explicit contiguous or
expected-stride layout validation for every tensor later passed as a raw pointer
in this binding, including output_page_table, lengths, optional row_to_batch,
row_starts, page_table_row_starts, and output_raw_indices. Add the checks
alongside the existing CHECK_INPUT_AND_TYPE/CHECK_DIM validation, or propagate
each tensor’s strides through the dispatch path; ensure shape-valid tensors with
incompatible storage layouts are rejected before pointer use.
🤖 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 `@flashinfer/trace/templates/__init__.py`:
- Around line 57-62: Update the Const behavior documentation near
build_fi_trace_fn to state that Const() writes an axis value only when tensor or
scalar extraction succeeds; if both return None and default is None, omit
axes[axis]["value"] from the generated JSON, while Const(default=N) still
supplies N.

---

Outside diff comments:
In `@csrc/topk.cu`:
- Around line 92-137: Require explicit contiguous or expected-stride layout
validation for every tensor later passed as a raw pointer in this binding,
including output_page_table, lengths, optional row_to_batch, row_starts,
page_table_row_starts, and output_raw_indices. Add the checks alongside the
existing CHECK_INPUT_AND_TYPE/CHECK_DIM validation, or propagate each tensor’s
strides through the dispatch path; ensure shape-valid tensors with incompatible
storage layouts are rejected before pointer 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: cdf1c194-9586-4c82-81a8-7e66660fdb9f

📥 Commits

Reviewing files that changed from the base of the PR and between 3c9627782a2345fddcae4aa51bb7de00ece837c0 and b8f701b8da5f1c863e356a70b14c074d3691f3ae.

📒 Files selected for processing (7)
  • csrc/topk.cu
  • flashinfer/trace/template.py
  • flashinfer/trace/templates/__init__.py
  • flashinfer/trace/templates/sampling.py
  • tests/trace/test_fi_trace.py
  • tests/trace/test_fi_trace_template_consistency.py
  • tests/utils/test_topk.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • flashinfer/trace/templates/sampling.py
  • tests/utils/test_topk.py

Comment thread flashinfer/trace/templates/__init__.py Outdated
@zianglih

zianglih commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up on the outside-diff native-layout note: the listed contracts are already enforced at the native FFI boundary. CHECK_INPUT_AND_TYPE expands to CHECK_CUDA, CHECK_CONTIGUOUS (TensorView::IsContiguous), and the dtype check, and radix_topk_page_table_transform applies it to output_page_table, lengths, row_to_batch, row_starts, page_table_row_starts, output_raw_indices, and src_page_table. The score input is intentionally the only padded-row exception: it requires last-dimension stride 1 plus nonoverlapping rows, and its row stride is passed into CUDA. Duplicating explicit stride checks would reject safe singleton layouts without strengthening the pointer contract. Current-head validation after the overload cleanup: editable CUDA 13.2 source build, all 1,463 Top-K tests, and pre-commit passed.

@bkryu

bkryu commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

@zianglih, merging your other PR has caused merge conflicts in two files.

Can you check and update the PR?

@zianglih

zianglih commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Hi @bkryu , github ci passed.

@bkryu

bkryu commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Hi @zianglih, the main branch and CI opened back to business, so I started merging the long backlog of PR and #3901 resulted in a merge conflict.

Would you be kind to resolve the conflict one last time? This PR is now at the front of my tracking list of "PRs to merge". Will keep an eye on it once resolved to ensure it gets in. I appreciate your patience with the PR

@ziang-and
ziang-and force-pushed the agent/dsv4-compact-page-topk branch from 0bd5a75 to cf0319a Compare August 8, 2026 17:44
@zianglih

zianglih commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 8, 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.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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: 2

🧹 Nitpick comments (1)
csrc/topk.cu (1)

189-193: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the dispatch-policy rationale.

Document why the direct policy remains on the default path and why the configurable policy is required for compact pages, raw indices, padded rows, and unaligned input. State that the direct-policy alternative preserves the existing direct-kernel ABI and code generation.

As per coding guidelines, document the rationale for special algorithmic choices and relevant alternatives in performance-critical hot paths.

Proposed comment
+  // Keep the direct policy for the packed page_size=1 ABI-compatible fast path.
+  // Use the configurable policy for compact pages, raw outputs, padded rows,
+  // or inputs that cannot safely use the direct policy's vectorization.
   const bool use_configurable_page_table_policy =
🤖 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/topk.cu` around lines 189 - 193, Document the dispatch-policy rationale
adjacent to use_configurable_page_table_policy: explain that the direct policy
remains the default because its alternative preserves the existing direct-kernel
ABI and code generation, while the configurable policy is required for compact
pages, raw indices, padded rows, and unaligned input. Keep the condition and
behavior unchanged.

Source: Coding guidelines

🤖 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/topk.cu`:
- Around line 135-140: Update the validation around maybe_output_raw_indices in
the top-k path to reject storage overlap with every kernel input and output,
including src_page_table and output_page_table, rather than relying only on
CHECK_SHAPE and CHECK_DEVICE. Reuse the project’s existing storage-overlap
validation utility if available, and preserve the current dtype, device,
dimension, and shape checks.
- Around line 189-193: Update the use_configurable_page_table_policy condition
to check input_stride multiplied by sizeof(dtype), not input_stride alone, when
detecting row-spanning alignment issues. Ensure non-aligned byte strides select
ConfigurablePageTableKernelPolicy so refine_vec_size() accounts for every row
base before LoadToSharedOrdered() vector loads.

---

Nitpick comments:
In `@csrc/topk.cu`:
- Around line 189-193: Document the dispatch-policy rationale adjacent to
use_configurable_page_table_policy: explain that the direct policy remains the
default because its alternative preserves the existing direct-kernel ABI and
code generation, while the configurable policy is required for compact pages,
raw indices, padded rows, and unaligned input. Keep the condition and behavior
unchanged.
🪄 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: 28b377be-132c-4f22-9ca9-a5aac5135d5a

📥 Commits

Reviewing files that changed from the base of the PR and between 29196cf and cf0319a.

📒 Files selected for processing (6)
  • csrc/flashinfer_topk_binding.cu
  • csrc/topk.cu
  • flashinfer/topk.py
  • flashinfer/trace/templates/sampling.py
  • include/flashinfer/topk.cuh
  • tests/utils/test_topk.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • csrc/flashinfer_topk_binding.cu
  • flashinfer/trace/templates/sampling.py
  • flashinfer/topk.py
  • include/flashinfer/topk.cuh

Comment thread csrc/topk.cu
Comment on lines +135 to +140
if (maybe_output_raw_indices.has_value()) {
CHECK_INPUT_AND_TYPE(maybe_output_raw_indices.value(), dl_int32);
CHECK_DEVICE(maybe_output_raw_indices.value(), input);
CHECK_DIM(2, maybe_output_raw_indices.value());
CHECK_SHAPE(maybe_output_raw_indices.value(), output_page_table);
}

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect public-buffer validation and existing aliasing tests.
rg -n -C6 'output_raw_indices|radix_topk_page_table_transform|data_ptr|overlap|alias' \
  flashinfer/topk.py csrc/flashinfer_topk_binding.cu tests/utils/test_topk.py

Repository: flashinfer-ai/flashinfer

Length of output: 19152


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== topk.cu outlines =="
ast-grep outline csrc/topk.cu --view expanded || true

echo "== relevant topk.cu sections =="
wc -l csrc/topk.cu
sed -n '1,220p' csrc/topk.cu
echo "== raw index references in topk.cu =="
rg -n -C4 'output_raw_indices|maybe_output_raw_indices|output_raw|page_table|raw_indices' csrc/topk.cu

Repository: flashinfer-ai/flashinfer

Length of output: 17521


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate TopKPageTableTransformDispatch and policies =="
rg -n -C8 'TopKPageTableTransformDispatch|ConfigurablePageTableKernelPolicy|DirectPageTableKernelPolicy|output_raw_indices_ptr|raw_indices_ptr' csrc flashinfer tests -g '*.cu' -g '*.cuh' -g '*.cpp' -g '*.h' -g '*.cuh' -g '*.py'

echo "== source snippets from candidate files =="
for f in $(rg -l 'ConfigurablePageTableKernelPolicy|TopKPageTableTransformDispatch' csrc flashinfer tests -g '*.cu' -g '*.cuh' -g '*.cpp'); do
  echo "--- $f ---"
  wc -l "$f"
  rg -n 'ConfigurablePageTableKernelPolicy|TopKPageTableTransformDispatch|TopKPageTable' "$f" | while IFS=: read -r lineno rest; do
    start=$((lineno-80)); [ "$start" -lt 1 ] && start=1; endl=$((lineno+120))
    sed -n "${start},${endl}p" "$f"
  done
done

echo "== validate_helper/overlap helpers =="
rg -n -C4 'CHECK_INPUT|data_ptr|range|overlap|alias|is_same|storage_offset|stride|size' csrc/tvm_ffi_utils.h csrc/topk.cu flashinfer/topk.py

Repository: flashinfer-ai/flashinfer

Length of output: 50380


Reject overlapping raw-index buffers.

CHECK_SHAPE and CHECK_DEVICE do not prevent output_raw_indices from overlapping other buffers such as src_page_table or output_page_table. If it overlaps src_page_table, kernel stores can modify page-table entries while other threads read them, producing nondeterministic translated indices. Reject storage-range overlap with all kernel inputs and outputs, or explicitly define safe aliasing 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/topk.cu` around lines 135 - 140, Update the validation around
maybe_output_raw_indices in the top-k path to reject storage overlap with every
kernel input and output, including src_page_table and output_page_table, rather
than relying only on CHECK_SHAPE and CHECK_DEVICE. Reuse the project’s existing
storage-overlap validation utility if available, and preserve the current dtype,
device, dimension, and shape checks.

Comment thread csrc/topk.cu
Comment on lines +189 to +193
const bool use_configurable_page_table_policy =
page_bits != 0 || output_raw_indices_ptr != nullptr ||
input_stride != static_cast<int64_t>(max_len) ||
(row_starts_ptr == nullptr && !dsa_graph_safe &&
reinterpret_cast<std::uintptr_t>(input.data_ptr()) % sampling::TOPK_MAX_VECTOR_BYTES != 0);

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
set -euo pipefail

# Map policy definitions before inspecting vector-load and alignment behavior.
ast-grep outline include/flashinfer/topk.cuh --items all \
  --match 'DirectPageTableKernelPolicy|ConfigurablePageTableKernelPolicy|TopKPageTableTransformDispatch'

rg -n -C8 \
  'DirectPageTableKernelPolicy|ConfigurablePageTableKernelPolicy|TOPK_MAX_VECTOR_BYTES|input_stride|VecSize|vector' \
  include/flashinfer/topk.cuh

# Locate coverage for non-vector-aligned row strides.
rg -n -C6 'misalign|stride|page_table_transform|page_size' tests/utils/test_topk.py

Repository: flashinfer-ai/flashinfer

Length of output: 50380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files under csrc with topk:"
fd -a 'topk|TopK' csrc include tests 2>/dev/null | sed 's#^\./##' | head -80

echo
echo "csrc/topk.cu dispatch lines:"
sed -n '150,230p' csrc/topk.cu

echo
echo "Relevant csrc/topk.cu policy dispatch if present:"
rg -n -C5 'use_configurable_page_table_policy|input\.data_ptr\(\)|input_stride|DirectPageTableKernelPolicy|ConfigurablePageTableKernelPolicy|TopKPageTableTransformDispatch|top_k_page_table_transform' csrc/topk.cu || true

echo
echo "Top-level dispatch in include around TopKPageTableTransformDispatch:"
sed -n '3620,3740p' include/flashinfer/topk.cuh

echo
echo "Multi-CTA vec-size refinement:"
sed -n '2088,2110p' include/flashinfer/topk.cuh

echo
echo "Filtered vec-size dispatch:"
sed -n '3399,3425p' include/flashinfer/topk.cuh

Repository: flashinfer-ai/flashinfer

Length of output: 20274


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Vector load definitions/uses in include/flashinfer/topk_common.cuh:"
rg -n -C4 'struct vec_t|cast_load|load|vectorized|__ldg' include/flashinfer/topk_common.cuh include/flashinfer/topk.cuh | head -240

echo
echo "Locate vec_t template and cast_load implementations:"
rg -n 'template .*vec_t|struct vec_t|cast_load|load_data|vectorize' include/flashinfer/topk_common.cuh include/flashinfer/topk.cuh

Repository: flashinfer-ai/flashinfer

Length of output: 12352


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Find topk_common include path and vec_t definition in repository files:"
rg -n -C8 'struct vec_t|template <typename DType|alignas|__ldg|__align__' include/flashinfer/topk_common.cuh | head -220

echo
echo "Candidate topk common outline:"
ast-grep outline include/flashinfer/topk_common.cuh --match vec_t --view expanded || true
rg -n 'vec_t|struct vec_t|cast_load' include/flashinfer/topk_common.cuh include/flashinfer/topk.cuh

Repository: flashinfer-ai/flashinfer

Length of output: 2200


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repository-wide vec_t definitions:"
rg -n -C8 'struct vec_t|template .*vec_t|alignas.*vec|__align__.*vec|cast_load' . --glob '*.{cuh,cu,cpp,h,py,hpp}' | head -260

echo
echo "topk_common.cuh beginning:"
sed -n '1,80p' include/flashinfer/topk_common.cuh

echo
echo "topk.cuh vec_t occurrences with surrounding context:"
sed -n '290,340p' include/flashinfer/topk.cuh
sed -n '535,625p' include/flashinfer/topk.cuh
sed -n '2620,2670p' include/flashinfer/topk.cuh

echo
echo "TopK entry wrapper / input_shape validation if present:"
rg -n -C6 'top_k_page_table_transform|input_shape|stride|page_size|input_stride' python flashinfer python include csrc -g '*.py' -g '*.cuh' -g '*.cu' | head -260

Repository: flashinfer-ai/flashinfer

Length of output: 50380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Check whether vec_t has include-only definition in generated build files / cache, excluding source data:"
git ls-files | rg '(^|/)(build|dist|third_party|venv|\.venv|flashinfer/_ffi|tmp_|_ffi|aot_output|torch|cuda)' | head -50 || true
find . -type f \( -name '*topk_common*.cuh' -o -name '*topk*.cuh' -o -name '*vec*.cuh' \) | head -100

echo
echo "topk.cuh includes at top:"
sed -n '1,40p' include/flashinfer/topk.cuh

echo
echo "Search for any cast_load/global load macro definitions in include/topk-common style files without excluding common topk.cuh definitions already scanned:"
rg -n -C12 'template <typename DType|template <uint32_t|struct vec_t|class vec_t|__forceinline__ vec_t|cast_load|__ldg|asm volatile|global_memory|read_via' include/flashinfer/topk_common.cuh include/flashinfer/topk.cuh | head -220

echo
echo "Python entry signature around top_k_page_table_transform:"
fd -a 'topk.py|flashinfer_topk' python flashinfer csrc include 2>/dev/null | head -100
rg -n -C12 'def top_k_page_table_transform|top_k_page_table_transform\(|top_k_page_table_transform_cython|__from_dlpack|input\.stride\(|input_stride' flashinfer csrc include --glob '*.{py,cu,cuh}' | head -200

Repository: flashinfer-ai/flashinfer

Length of output: 38360


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "vec_dtypes.cuh outline and vector definitions:"
wc -l include/flashinfer/vec_dtypes.cuh
sed -n '1,220p' include/flashinfer/vec_dtypes.cuh
rg -n -C16 'template <typename T|template <typename T,|struct vec_t|alignas|(using|typedef).*load_value|cast_load|load_value|read_via|global_memory|__ldg|asm volatile' include/flashinfer/vec_dtypes.cuh include/flashinfer/topk.cuh include/flashinfer/topk_common.cuh | sed -n '1,260p'

echo
echo "Python topk dispatch and stride check:"
sed -n '300,530p' flashinfer/topk.py
sg -n 'input\.stride\(|input_stride|top_k_page_table_transform|page_size|DSA|row_starts' flashinfer/topk.py csrc/flashinfer_topk_binding.cu csrc/topk.cu include/flashinfer/*.cuh | head -160 || true

Repository: flashinfer-ai/flashinfer

Length of output: 33422


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Search vec_t definition in vec_dtypes.cuh sections excluding first 220 lines:"
rg -n -C24 'template <typename T, typename|template <typename T|template <typename T,\s*uint32_t|class vec_t|struct vec_t|using vec_t|__ldg|__align__|alignas\(.*vec|cast_load\(|load_value\(|global_memory|read_via' include/flashinfer/vec_dtypes.cuh | sed -n '1,260p'

echo
echo "Show around cast_load definitions in vec_dtypes.cuh:"
rg -n 'cast_load|load_value|global_memory|read_via|ld\.global|ld\.cg|__ldg' include/flashinfer/vec_dtypes.cuh
if [ -n "$(rg -n 'cast_load' include/flashinfer/vec_dtypes.cuh)" ]; then
  rg -n -C16 'cast_load' include/flashinfer/vec_dtypes.cuh | sed -n '1,260p'
fi

echo
echo "TopK entry registration call site:"
rg -n -C12 'radix_topk_page_table_transform\(|top_k_page_table_transform|input_stride|input\.stride\(' csrc/flashinfer_topk_binding.cu flashinfer/topk.py python flashinfer csrc --glob '*.{cu,cuh,py}' | sed -n '1,220p'

Repository: flashinfer-ai/flashinfer

Length of output: 36727


🌐 Web query:

CUDA unaligned global memory load vector float2 float4 undefined behavior

💡 Result:

In CUDA, accessing global memory with an unaligned address—specifically for vector types like float2 or float4—is classified as undefined behavior [1][2][3]. Hardware requirements dictate that an N-byte data object must be accessed at an address that is a multiple of N bytes (natural alignment) [4][3]. For vector types, this means: - float2 (8 bytes) requires 8-byte alignment [4]. - float4 (16 bytes) requires 16-byte alignment [5][4]. If these alignment requirements are violated, the behavior is explicitly undefined [1][6][3]. In practice, this may result in: - Silent data corruption (e.g., the hardware may mask off lower-order address bits to force alignment) [5][6]. - Incorrect results [5]. - Hardware exceptions or faults, which can crash the kernel or the application [5][6]. Because the GPU hardware does not support unaligned loads and stores, there is no "safe" way to perform an unaligned access using vector types [4][1]. Simply casting a float* pointer to a float4* pointer and dereferencing it will lead to undefined behavior if the base address is not a multiple of 16 [5][4]. To ensure correct and efficient operation, developers must manually manage data alignment or use multiple smaller, naturally aligned loads (e.g., loading individual floats) [5][4]. Tools such as cuda-memcheck can be used to detect these invalid memory access violations during development [2][3].

Citations:


Check input_stride * sizeof(dtype) before selecting the direct policy.

With input_stride = max_len + 1 and max_len odd, a contiguous fp16 row can be aligned at input.data_ptr() but unaligned at each subsequent row. The direct policy still gets a vector size of gcd(8, max_len+1) and then refines only on the first row pointer; LoadToSharedOrdered() casts the row address to a vector load. Route row-spanning non-aligned strides to ConfigurablePageTableKernelPolicy so refine_vec_size() can reduce the vector width for every row base.

🤖 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/topk.cu` around lines 189 - 193, Update the
use_configurable_page_table_policy condition to check input_stride multiplied by
sizeof(dtype), not input_stride alone, when detecting row-spanning alignment
issues. Ensure non-aligned byte strides select ConfigurablePageTableKernelPolicy
so refine_vec_size() accounts for every row base before LoadToSharedOrdered()
vector loads.

@zianglih

zianglih commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

Hi @bkryu , I have finished rebasing and refreshed the perf numbers. Overhead is still nigligible. Thanks!

@bkryu

bkryu commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

/bot run tests/utils

@flashinfer-bot

Copy link
Copy Markdown
Collaborator

GitLab MR !1160 has been updated with latest changes, and the CI pipeline #61740159 is currently running. I'll report back once the pipeline job completes.

@zianglih

zianglih commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

@bkryu
bkryu merged commit 7f614b8 into flashinfer-ai:main Aug 9, 2026
34 of 45 checks passed
@bkryu

bkryu commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

@zianglih thank you the CI result looks good. Merged!

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. run-ci

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants