Skip to content

Make the public gemm path work for the flavors it claims - #558

Merged
YangXu1990uiuc merged 4 commits into
NVIDIA:developfrom
YangXu1990uiuc:yanxu/gemm-reduction-public-path
Aug 13, 2026
Merged

Make the public gemm path work for the flavors it claims#558
YangXu1990uiuc merged 4 commits into
NVIDIA:developfrom
YangXu1990uiuc:yanxu/gemm-reduction-public-path

Conversation

@YangXu1990uiuc

@YangXu1990uiuc YangXu1990uiuc commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #547, which landed before this was ready.

#547 changed what an engine is handed: buffers arrive as slots that describe memory rather than as tensors. Two places in the gemm engine write THROUGH the caller's buffer with torch methods, which worked only while the buffer happened to be a torch tensor.

A reduction output is broken on develop right now. The epilogue seeds it with tensor.fill_() before the kernel runs; a slot has no fill_. The engine owns that operation now -- every seed a reduction uses (0, 1, ±inf) is a 32-bit pattern, so cuMemsetD32Async covers them without a kernel.

norm2's sqrt_ finalize is unreachable: the backend refuses that reduction while the graph is lowered, so no plan exists to execute. Recorded as a test rather than left looking live.

A bare device address as the workspace measured 0 bytes and Workspace.over read that as "empty", refusing any engine that needs scratch. Zero means "the pack could not measure it" -- a raw pointer carries no size, and the backend takes one without checking.

Why the suite did not catch it

No test drove a non-trivial gemm flavor through graph.execute(). The tests that cover reductions build a fusion chain and call the compiled object directly with torch tensors, which skips operand binding, the pack, and the conversion execute() performs -- exactly the part #547 changed.

test_public_execute_flavors.py closes that: plain matmul, epilogue fusion, the four reduction modes, norm2's refusal, and the bare-address operand form, all through the public entry point.

The bare-address case is xfail(strict): frost reads its extents by axis position, and a bare address describes the operand the way the GRAPH declares it (a matmul's B is [batch, K, N]) rather than the way a caller's buffer reports it. Broken before #547 too, as an IndexError. The fix is the engine recording which axis is M/N/K at build, which belongs with the executor rewrite.

Verified on SM100: 4557 passed, 1 xfailed across gemm/, linear_attention/ and the variant-pack suites.

note to self: claude::774e8e99-23ad-4a94-be0d-53ed5ee4def9 — "cuDNN FE variant-pack normalization" · cwd /home/scratch.yanxu_libs/cudnn_frontend · workspace /home/scratch.yanxu_gpu/fe_pr1

Summary by CodeRabbit

  • New Features

    • Added asynchronous device-memory initialization for supported 32-bit floating-point and integer values, including supported strided layouts.
    • Improved reduction-output initialization across CUDA execution streams.
    • Improved handling of workspaces with unknown sizes while preserving validation for known undersized workspaces.
  • Tests

    • Added public FROST GEMM coverage for matmul, fused operations, reductions, raw-address operands, strided fills, and workspace validation.
  • Documentation

    • Clarified runtime descriptor construction and supported tensor exchange behavior.

@coderabbitai

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

This change adds asynchronous 32-bit device fills for FROST reduction outputs, passes runtime streams through GEMM launchers, treats zero workspace sizes as unknown, adds public execution tests, and updates runtime documentation.

Changes

FROST execution

Layer / File(s) Summary
Stream-aware reduction and workspace handling
python/cudnn/frost/buffers.py, python/cudnn/frost/workspace.py, python/cudnn/gemm/frost/compiler.py, python/pygraph/variant_pack.cpp
Reduction outputs use dtype-specific asynchronous 32-bit CUDA fills with runtime streams. Strided layouts are collapsed and validated. Zero workspace sizes skip capacity checks, while tail measurement raises ValueError.
Public FROST execution coverage
test/python/gemm/frost/test_public_execute_flavors.py
Tests cover matmul, ReLU fusion, supported reductions, INT32 reduction seeding, rejected NORM2 reductions, raw-address operands, workspace-size handling, layout validation, strided fills, and stream-safe padded-output initialization.
Runtime documentation and import cleanup
python/cudnn/engines/base.py, python/cudnn/linear_attention/frost/kernel/*.py, python/pygraph/variant_pack.cpp
Documentation describes per-execution descriptor construction, DLPack capsule behavior, and unknown workspace sizes. Unused imports were removed.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: 🟡 Moderate · up to 99c44

The PR enables reduction-output initialization through the public GEMM path, but validation of a later output can currently fail after an earlier caller buffer has already been modified, leaving partial state when execution aborts. This bounded correctness risk should be fixed or explicitly accepted before merge; a separate Python formatting follow-up remains outstanding.

Sequence Diagram(s)

sequenceDiagram
  participant GEMMCompiler
  participant fill_word_strided_async
  participant CUDADriver
  GEMMCompiler->>fill_word_strided_async: initialize reduction output on runtime stream
  fill_word_strided_async->>CUDADriver: issue asynchronous 32-bit memset
  CUDADriver-->>fill_word_strided_async: return CUDA status
  fill_word_strided_async-->>GEMMCompiler: complete or raise RuntimeError
Loading

Possibly related PRs

  • NVIDIA/cudnn-frontend#559: Shares the FROST buffer-fill, workspace, GEMM reduction initialization, and public execution test changes.

Suggested labels: orig-nv-eng, mod-frost, cat-enhancements

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.47% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: supporting the public GEMM execution path for its claimed flavors.
Description check ✅ Passed The description explains the problem, implementation, rationale, related issue, compatibility considerations, and test results, but omits some template headings and exact commands.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

🤖 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 `@python/cudnn/frost/workspace.py`:
- Around line 128-132: Update the Workspace handling around take() and
remaining() so a workspace with unknown capacity (workspace_bytes == 0) cannot
produce an invalid negative-sized DeviceView: either reject remaining()
explicitly or represent the remaining tail as unknown. Preserve valid behavior
for known capacities, and add coverage for a raw workspace address followed by
take() and remaining().

In `@python/cudnn/gemm/frost/compiler.py`:
- Around line 1775-1779: Update the reduction-output initialization in the
visible fill branch to honor the supplied stream: replace the direct torch fill
path with stream-aware fill_f32_async, or execute fill_ under a
torch.cuda.ExternalStream backed by ctx.stream. Preserve the existing fallback
for non-torch tensors and add a regression test covering differing current and
supplied CUDA streams.

In `@test/python/gemm/frost/test_public_execute_flavors.py`:
- Around line 62-65: Update the test helper or relevant tests around _run and
test_bare_address_operands to execute a graph requiring FROST workspace with
ws.data_ptr() rather than always passing the tensor. Add a passing bare-address
case that exercises the unknown-capacity path in Workspace.over and
WorkspaceCarve.carve, while retaining a known undersized workspace tensor case
that verifies bounds validation still raises.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 038ac86c-9595-45cf-92bc-a05a3a9f54d4

📥 Commits

Reviewing files that changed from the base of the PR and between 66efedf and 722770b.

📒 Files selected for processing (9)
  • python/cudnn/engines/base.py
  • python/cudnn/frost/buffers.py
  • python/cudnn/frost/workspace.py
  • python/cudnn/gemm/frost/compiler.py
  • python/cudnn/linear_attention/frost/kernel/gdn2_prefill_f16.py
  • python/cudnn/linear_attention/frost/kernel/gdn_bprop_f16.py
  • python/cudnn/linear_attention/frost/kernel/kda_prefill_f16.py
  • python/pygraph/variant_pack.cpp
  • test/python/gemm/frost/test_public_execute_flavors.py
💤 Files with no reviewable changes (1)
  • python/cudnn/engines/base.py

Comment thread python/cudnn/frost/workspace.py
Comment thread python/cudnn/gemm/frost/compiler.py Outdated
Comment thread test/python/gemm/frost/test_public_execute_flavors.py
@YangXu1990uiuc

Copy link
Copy Markdown
Collaborator Author

@cudnn-ci-bot run frost

@cudnn-ci-bot

Copy link
Copy Markdown

🚀 Running mirror pipeline

Branch: cudnn-gh/pr-558-722770b
Pipeline: 62275026
Targets: frost

@YangXu1990uiuc

Copy link
Copy Markdown
Collaborator Author

Pushed 897152cea. The reduction seed went through three passes, and only the third is mergeable — worth recording because the suite was green at the second:

  1. tensor.fill_() does not exist on a slot → cuMemsetD32Async.
  2. That lost the stride. torch's fill_ sets every element; a memset writes a contiguous byte range, and the two agree only for a dense buffer. Six strided reduction tests went NaN. Caught by the existing coverage.
  3. One memset per contiguous run is correct — and 572 µs for a per-row scalar output against 3.5 µs for the single kernel torch launches. The whole gemm execute is 18–44 µs. Every test passed at this point; nothing in the suite would ever have flagged it.

Landed: the driver where it is right, which is every contiguous buffer, and that also puts the seed on the stream the kernel runs on. A padded output keeps the torch path it already had. A padded output arriving as a slot — the public path, where this was broken and where nothing could seed it — is refused, with the measurement in a TODO pointing at the fill kernel that would close it.

Verified: 4494 passed, 1 xfailed across gemm/, linear_attention/ and the variant-pack suites.

The general point, since it cost three passes: replacing a borrowed operation means adopting a contract you did not write. fill_ promises "set every element"; a memset promises "write N contiguous words". I substituted one for the other on the strength of what it did in the case in front of me.

@YangXu1990uiuc

Copy link
Copy Markdown
Collaborator Author

@cudnn-ci-bot run frost

@cudnn-ci-bot

Copy link
Copy Markdown

🚀 Running mirror pipeline

Branch: cudnn-gh/pr-558-897152c
Pipeline: 62280387
Targets: frost

@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

🤖 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 `@test/python/gemm/frost/test_public_execute_flavors.py`:
- Around line 190-210: Assign an L0–L4 test-level marker to each new test:
test/python/gemm/frost/test_public_execute_flavors.py:190-210
test_bare_address_workspace, :214-233 test_undersized_workspace_still_rejected,
and :236-254 test_unknown_size_workspace_refuses_to_measure_its_tail. Use the
project’s established Python test-level marker convention for all three tests.
- Line 231: Update the pytest.raises call in the relevant test to use a raw
regex string for the match pattern, preserving the existing regular expression
while satisfying Ruff RUF043.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f5d319b3-30dc-4443-8bf0-1fc59f1cee36

📥 Commits

Reviewing files that changed from the base of the PR and between 722770b and 897152c.

📒 Files selected for processing (4)
  • python/cudnn/frost/buffers.py
  • python/cudnn/frost/workspace.py
  • python/cudnn/gemm/frost/compiler.py
  • test/python/gemm/frost/test_public_execute_flavors.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • python/cudnn/frost/buffers.py
  • python/cudnn/frost/workspace.py
  • python/cudnn/gemm/frost/compiler.py

Comment thread test/python/gemm/frost/test_public_execute_flavors.py
Comment thread test/python/gemm/frost/test_public_execute_flavors.py Outdated
``tensormap_workspace``. Compiled + launched separately from the main
kernel and cached by input identity in the host bridge, so the builder
launches do not recur in steady-state replay.
``tensormap_workspace``.Launched on every execute: the descriptors fold cu_seqlens contents into

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: missing space

Suggested change
``tensormap_workspace``.Launched on every execute: the descriptors fold cu_seqlens contents into
``tensormap_workspace``. Launched on every execute: the descriptors fold cu_seqlens contents into

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — thank you. There was a third occurrence you did not hit, gdn2_prefill_f16.py, so all three are corrected rather than just the two flagged.

o, h) into ``tensormap_workspace``. Compiled + launched separately from
the main kernel and cached by input identity in the host bridge, so the
builder launches do not recur in steady-state replay. Each descriptor
o, h) into ``tensormap_workspace``.Launched on every execute: the descriptors fold cu_seqlens contents into

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: missing space

Suggested change
o, h) into ``tensormap_workspace``.Launched on every execute: the descriptors fold cu_seqlens contents into
o, h) into ``tensormap_workspace``. Launched on every execute: the descriptors fold cu_seqlens contents into

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — thank you. There was a third occurrence you did not hit, gdn2_prefill_f16.py, so all three are corrected rather than just the two flagged.

@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

🤖 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 `@python/cudnn/gemm/frost/compiler.py`:
- Line 1781: Update the padded-output handling around the reduction output fill
to remove the tensor.fill_() fallback and use buffers.fill_word_async for every
variant-pack buffer type, preserving the reduction dtype/value and passing the
target stream so initialization is stream-ordered.

In `@test/python/gemm/frost/test_public_execute_flavors.py`:
- Around line 140-141: Add the appropriate L0–L4 test-level marker to
test_int32_reduction_seed_is_packed_as_int32, while retaining the existing `@_GPU`
hardware gate.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 140e34bb-d983-457d-8f43-79d56b7cfbe8

📥 Commits

Reviewing files that changed from the base of the PR and between 897152c and dee80c0.

📒 Files selected for processing (3)
  • python/cudnn/frost/buffers.py
  • python/cudnn/gemm/frost/compiler.py
  • test/python/gemm/frost/test_public_execute_flavors.py

Comment thread python/cudnn/gemm/frost/compiler.py Outdated
Comment thread test/python/gemm/frost/test_public_execute_flavors.py
YangXu1990uiuc and others added 3 commits August 12, 2026 16:33
NVIDIA#547 normalized the variant pack, which changed what an engine is handed:
buffers arrive as slots that describe memory rather than as tensors. Two places
in the gemm engine were writing THROUGH the caller's buffer with torch methods,
which worked only while the buffer happened to be a torch tensor:

  - a reduction output is seeded with its identity before the kernel runs,
    via tensor.fill_()
  - a norm2 reduction is finalized with tensor.sqrt_()

The engine owns the first one now. Every seed a reduction uses (0, 1, +-inf) is
a 32-bit pattern, so buffers.fill_f32_async drives cuMemsetD32Async and no
kernel is needed. The second is unreachable: a norm2 reduction is refused while
the graph is lowered, so no plan exists to execute -- recorded as a test rather
than left as a live-looking path.

A bare device address as the WORKSPACE measured 0 bytes, and Workspace.over
read that as "empty" and refused any engine that needs scratch. It means "the
pack could not measure it": a raw pointer carries no size and the backend takes
one without checking, so refusing here made the same call depend on which plan
ran. Zero now skips the size check, here and in the C carve's bounds check.

The reason none of this was caught: no test drove a non-trivial gemm flavor
through graph.execute(). The direct-call tests construct a fusion chain and
invoke the compiled object with torch tensors, which skips operand binding, the
pack, and the conversion execute() performs -- exactly the part that changed.
test_public_execute_flavors.py covers plain matmul, epilogue fusion, the four
reduction modes, norm2's refusal, and the bare-address operand form through the
entry point a caller actually has.

That last one is xfail: frost reads its extents by axis position, and a bare
address describes the operand the way the GRAPH declares it (a matmul's B is
[batch, K, N]) rather than the way a caller's buffer reports it. It was broken
before this too -- the geometry-less Tensor made it an IndexError instead. The
fix is the engine recording which axis is M/N/K at build.

Also: two unused imports in engines/base.py that broke the lazy frost boundary,
and three kernel docstrings claiming the descriptor builders do not recur.
Three passes over the same fix, each caught by measuring rather than by the
suite going green:

  1. tensor.fill_() does not exist on a slot -> use cuMemsetD32Async.
  2. That lost the STRIDE. torch's fill_ sets every element; a memset writes a
     contiguous byte range, and the two agree only for a dense buffer. Six
     strided reduction tests went NaN -- the elements past the first run were
     never written.
  3. One memset per contiguous run is correct and 572 us for a per-row scalar
     output, against 3.5 for the single kernel torch launches. Correct is not
     mergeable; nothing in the suite would have flagged it.

So: the driver where it is right, which is every contiguous buffer -- and that
also puts the seed on the stream the kernel will run on, where tensor.fill_()
queues on torch's current stream and is the same stream only by luck.

A padded output keeps the torch path it already had, and a padded output
arriving as a slot -- the public path, which is where this was broken and
where nothing could seed it -- is refused with the measurement in a TODO. The
fix is a fill kernel; this is the last place the engine writes through the
caller's buffer.

Also from review: remaining() refuses rather than returning a negative extent
when the workspace size is unknown, and three cases cover the unknown-capacity
path end to end (bare-address workspace, undersized-but-known, and the tail
refusal).
…stream

A memset moves bits, not numbers, so the identity has to be packed as the dtype
the kernel reads it back as. int32's identities are the ends of its range and
are exactly where that bites: -2**31 packed as float is 0xcf000000, so an int32
MAX reduction returned -822083584 for every input below it. fill_f32_async
becomes fill_word_async over a 32-bit pattern, with init_word turning a value
into one.

The four MoE launchers seeded on the null stream rather than the execute one --
they were the call sites that had no stream to pass before this path moved to
the driver, and passing None was not the same thing afterwards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@YangXu1990uiuc
YangXu1990uiuc force-pushed the yanxu/gemm-reduction-public-path branch from dee80c0 to 605e443 Compare August 12, 2026 23:40
@YangXu1990uiuc

Copy link
Copy Markdown
Collaborator Author

Force-pushed dee80c0ea605e443f1: rebase only, onto f06f1efaa after #564 / #544 / #565 / #566 landed. No content change — I diffed the two diffs and the only difference is line offsets and blob hashes.

The one file this PR shares with those merges is compiler.py, and the regions are disjoint: they changed the rendering / tile-config / support-gate half, this changes the reduction seed path.

test/python/gemm/frost on the rebased stack: 5766 passed / 2860 skipped / 0 failed.

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

Caution

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

⚠️ Outside diff range comments (1)
python/cudnn/gemm/frost/compiler.py (1)

2334-2335: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Format these validation errors with Black.

Lines 2335 and 2346 exceed the 160-character limit.

Proposed formatting
-            raise ValueError(f"TileConfig {config.name!r} cannot use M-major A: " f"per-MMA M={slice_m} is not divisible by " f"swizzle group {mn_group_elems}")
+            raise ValueError(
+                f"TileConfig {config.name!r} cannot use M-major A: "
+                f"per-MMA M={slice_m} is not divisible by "
+                f"swizzle group {mn_group_elems}"
+            )
@@
-            raise ValueError(
-                f"TileConfig {config.name!r} cannot use N-major B: " f"per-MMA per-CTA SMEM N={slice_n} is not divisible by " f"swizzle group {mn_group_elems}"
-            )
+            raise ValueError(
+                f"TileConfig {config.name!r} cannot use N-major B: "
+                f"per-MMA per-CTA SMEM N={slice_n} is not divisible by "
+                f"swizzle group {mn_group_elems}"
+            )

As per coding guidelines, format Python code with Black and a maximum line length of 160 characters.

Also applies to: 2344-2346

🤖 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 `@python/cudnn/gemm/frost/compiler.py` around lines 2334 - 2335, Reformat the
long ValueError validation messages in the relevant TileConfig validation block,
including the checks around slice_m and the lines 2344–2346, using
Black-compatible wrapping while preserving the existing error text and behavior.
Keep the maximum line length at 160 characters.

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.

Outside diff comments:
In `@python/cudnn/gemm/frost/compiler.py`:
- Around line 2334-2335: Reformat the long ValueError validation messages in the
relevant TileConfig validation block, including the checks around slice_m and
the lines 2344–2346, using Black-compatible wrapping while preserving the
existing error text and behavior. Keep the maximum line length at 160
characters.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: dca8b720-549c-454f-9b77-b1bde3450144

📥 Commits

Reviewing files that changed from the base of the PR and between dee80c0 and 605e443.

📒 Files selected for processing (1)
  • python/cudnn/gemm/frost/compiler.py

The contiguous case already went through cuMemsetD32Async on the execute-time
stream. The padded one fell back to tensor.fill_(), which queues on torch's
CURRENT stream -- the same stream only by luck -- and exists at all only while
the caller happened to pass a torch tensor. That contradicted what this branch
claims to do, so it is gone.

strided_fill_plan collapses the layout to the runs a memset can cover and
returns the 2D memsets that cover it exactly once. cuMemsetD2D32Async takes a
pitch, so a per-row scalar tap is ONE call rather than one per row -- that
reading, 572 us at one memset per row, is why the fallback was there. What
remains is one call per point of whatever axis is left outside the 2D region,
which for a rank-3 output is the batch and is usually 1.

The plan is returned before anything is written, and is None for a layout that
would write an element twice: a stride of 0 over a real extent, or an outer
stride that does not clear the axis below it. Checking only the innermost pair
(pitch >= width) is not enough -- shape (2, 2) stride (2, 2) has width 1 and
lands both axes on element 2.

Also in this commit: a missing space in three linear-attention kernel
docstrings (review caught two of the three), and a raw-string pytest.raises
pattern.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

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

lgtm and my agent.

@YangXu1990uiuc

Copy link
Copy Markdown
Collaborator Author

Pushed 99c44422b. All six open threads addressed — five fixed, one I am pushing back on.

Fixed

  • The padded reduction seed no longer goes through tensor.fill_(). This was the one that mattered, and I fixed it here rather than pointing at the stacked PR: this branch's own commit says "on the kernel's stream", so a path that queues on torch's contradicted what it claims to do. strided_fill_plan collapses the layout to the runs a memset can cover and cuMemsetD2D32Async takes a pitch, so a per-row scalar tap is one call rather than one per row — that reading (572 µs at one memset per row) is why the fallback existed. What remains is one call per point of whatever axis is left outside the 2D region: the batch, usually 1.

    Two things beyond the swap. The plan is produced before anything is written, because a seed that fails halfway has already scribbled on a caller's buffer. And the non-overlap rule is not just the innermost pair — pitch >= width accepts shape (2,2) stride (2,2), whose width is 1 and whose two axes both land on element 2; the rule is that each axis clears the whole span of the one below it.

  • Missing space in the kernel docstrings — there was a third occurrence in gdn2_prefill_f16.py that was not flagged, so all three are corrected.

  • Raw-string pytest.raises pattern.

Not changed, with reasoning

  • The "add an L0–L4 marker" comments are, I believe, false positives: line 24 is pytestmark = pytest.mark.L0, which pytest applies module-wide, and that is how every sibling file in this directory does it. Verified by collection rather than by reading — -m L0 --collect-only picks up all three named tests. Happy to be corrected if the guideline means something stricter.

Test: test/python/gemm/frost — 5767 passed / 2860 skipped / 0 failed on the stack. test_public_execute_flavors.py alone: 22 passed / 1 xfailed. New coverage is the collapse table, three rejected layouts, an exact write-set check against torch.as_strided(...).fill_() (asserting the elements between the strided ones are untouched), and a padded tap driven end to end through graph.execute() with canary padding.

Also rebased onto f06f1efaa earlier — see the note above; that part was content-identical.

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

Caution

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

⚠️ Outside diff range comments (1)
python/cudnn/gemm/frost/compiler.py (1)

1813-1829: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate all reduction outputs before writing any output.

Line 1829 plans and applies each strided fill during the loop. If an earlier output is valid and a later output has an unsupported or overlapping layout, the earlier caller buffer is modified before execution raises.

Build and validate all fill descriptors first. Apply the descriptors only after validation succeeds for every reduction output. Add a multi-reduction regression test with a valid first output and a rejected second output.

🤖 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 `@python/cudnn/gemm/frost/compiler.py` around lines 1813 - 1829, The
reduction-output loop must not mutate caller buffers before every layout is
validated. In the output initialization flow around the reduction descriptor
construction, first collect and validate all fill descriptors for every
reduction output, rejecting unsupported or overlapping layouts; only after
validation succeeds for the entire set should you invoke the contiguous or
strided fill helpers. Add a regression test covering multiple reductions where
the first output is valid and the second is rejected, verifying no output was
modified.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@python/cudnn/gemm/frost/compiler.py`:
- Around line 1813-1829: The reduction-output loop must not mutate caller
buffers before every layout is validated. In the output initialization flow
around the reduction descriptor construction, first collect and validate all
fill descriptors for every reduction output, rejecting unsupported or
overlapping layouts; only after validation succeeds for the entire set should
you invoke the contiguous or strided fill helpers. Add a regression test
covering multiple reductions where the first output is valid and the second is
rejected, verifying no output was modified.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6207cf32-e1f0-4d93-9ec2-4822efc27c68

📥 Commits

Reviewing files that changed from the base of the PR and between 605e443 and 99c4442.

📒 Files selected for processing (6)
  • python/cudnn/frost/buffers.py
  • python/cudnn/gemm/frost/compiler.py
  • python/cudnn/linear_attention/frost/kernel/gdn2_prefill_f16.py
  • python/cudnn/linear_attention/frost/kernel/gdn_bprop_f16.py
  • python/cudnn/linear_attention/frost/kernel/kda_prefill_f16.py
  • test/python/gemm/frost/test_public_execute_flavors.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • python/cudnn/linear_attention/frost/kernel/gdn_bprop_f16.py
  • python/cudnn/linear_attention/frost/kernel/gdn2_prefill_f16.py
  • python/cudnn/linear_attention/frost/kernel/kda_prefill_f16.py

@YangXu1990uiuc
YangXu1990uiuc merged commit eb43b2c into NVIDIA:develop Aug 13, 2026
1 check passed
@YangXu1990uiuc YangXu1990uiuc added cat-bug Reports of incorrect behavior, crashes, regressions, or unexpected results. orig-nv-eng Reported or requested by NVIDIA engineering. labels Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cat-bug Reports of incorrect behavior, crashes, regressions, or unexpected results. orig-nv-eng Reported or requested by NVIDIA engineering.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants