You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Triton/Gluon] Add 3D neighborhood flash attention (na3d_flash) - #5230
Video diffusion models such as LTX-2.5 use 3D neighborhood attention in
the VAE diffusion decoder: each token at grid position (t, h, w) attends
only to its (KT x KH x KW) neighbors rather than the full sequence. This
PR adds a BF16 MFMA flash-style kernel for that operation targeting gfx942
and gfx950, plus a reference, tests and benchmarks.
Use case example
LTX-2.5 Diffusion decoder attention processor in xDiT inference framework.
_na3d_sdpa_exact(q, k, v, kernel_size, w_offset=0) - Reference method. Exact per-query
neighborhood SDPA. Groups queries by shared relative-window geometry and
calls F.scaled_dot_product_attention per group with a cached additive mask.
With w_offset=0 and full q/k/v this is equivalent to a per-query FP32
reference but runs on the GPU. With w_offset>0, q is a W sub-slice and
k/v are the full tensors; neighborhood bounds use global W coordinates.
test_na3d_sdpa_exact_vs_ref: 6 small shapes; cross-checks _na3d_sdpa_exact
against the FP32 per-query loop
op_tests/op_benchmarks/triton/bench_na3d_flash.py
Performance benchmark for 3D neighborhood flash attention (na3d_flash) that compares against _na3d_sdpa_exact reference method from aiter/op_tests/triton_tests/attention/test_na3d_flash.py.
Measures TFLOPS and TB/s per shape
aiter/ops/triton/configs/gfx950/triton/attention/na3d_flash/DEFAULT.json: Tuned config for gfx950
aiter/ops/triton/configs/gfx942/triton/attention/na3d_flash/DEFAULT.json: Tuned config for gfx942
github-actionsBot
changed the title
Add 3D neighborhood flash attention (na3d_flash)
[Triton/Gluon] Add 3D neighborhood flash attention (na3d_flash)
Sep 3, 2026
All standard extended tests (excludes ci:atom_full)
Only add ci:atom_full for FlyDSL or Triton upgrades.
Add labels via the sidebar or gh pr edit 5230 --add-label <label>
PR title tags & labels:
Component tags ([Triton/Gluon], [HIP], [CK], [ASM], ...) are added to the PR title and as PR labels automatically from the changed files and re-synced on every push — change-type tags like [fix]/[Perf], op tags like [MLA], and human labels (ci:*) are left untouched. Add the no-auto-title label to opt this PR out.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
There are confirmed correctness/robustness issues in the correction/reference tiling logic and kernel-size validation, plus missing kernel repr= wiring needed for consistency with existing tracing conventions.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a new Triton-based BF16 “flash-style” 3D neighborhood attention implementation (na3d_flash) for video diffusion decoder-style workloads, alongside an exact PyTorch SDPA reference path and a dedicated correctness/benchmark test suite under the attention test category.
Changes:
Introduces a new autotuned Triton forward kernel for 3D neighborhood attention (_na3d_flash_fwd).
Adds a public launcher na3d_flash_attn(...) plus an exact grouped-SDPA reference helper (_na3d_sdpa_exact) used for validation and optional rightmost-block correction.
Adds pytest coverage and a benchmark harness for representative LTX-2.5 and edge-case shapes.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The exact-reference tiling logic can underestimate KV footprint for rightmost-block correction (risking OOM/timeouts), and the new kernel/tests violate established wrapper/kernel-boundary + kernel repr conventions.
Review details
Suppressed comments (5)
Previously missed (2) — in code that hasn't changed since the last review.
aiter/ops/triton/attention/na3d_flash.py:65
_na3d_sdpa_mask does int(en.max()) where en is a CUDA tensor; converting that to int forces a device sync. Since ends is already a Python tuple, you can compute the max end on CPU and avoid the sync. aiter/ops/triton/attention/na3d_flash.py:127
In _na3d_sdpa_exact, the tiling _cost() and split heuristic uses W_sub (and eff_kw = min(KW, W_sub)) to estimate KV footprint. When correct_rightmost_block=True, q_right can have W_sub < KW, but KV still spans a full KW window in global-W coordinates; this can severely underestimate nk, leading to oversized tiles, large intermediate k_s/v_s tensors, and potential OOM/timeouts.
New launchable Triton kernels should set a config-aware repr via make_kernel_repr (see aiter/ops/triton/README.md and the repo instructions). _na3d_flash_fwd currently lacks a repr, which makes profiling/trace names harder to interpret.
Tests should not import kernel bodies from aiter/ops/triton/_triton_kernels/... (wrapper-only boundary). Here the test imports _na3d_flash_fwd directly just to read best_config; instead, access the already-imported symbol from the public wrapper module.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The current kernel program-id to (t,h,w) mapping can cross W-row boundaries and break correctness due to scalar-reduced (t,h) window starts, and the launcher/correction paths also need alignment/guarding for correctness and predictable performance.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (4)
Previously missed (2) — in code that hasn't changed since the last review.
aiter/ops/triton/attention/na3d_flash.py:50
_na3d_sdpa_mask caches GPU-resident additive masks keyed by (rel_bounds, dtype, device). Since the tile heuristic only bounds nq*nk to 2**25, a single cached mask can be ~64MB (bf16), and maxsize=256 can retain many GB of GPU memory across calls/shapes. Consider reducing the cache size (or disabling it) to avoid unexpected OOMs when correct_rightmost_block/_na3d_sdpa_exact are used repeatedly. aiter/ops/triton/attention/na3d_flash.py:251
The launcher grid currently uses cdiv(SEQ, BLOCK_Q), which matches the old 1D q_idx mapping. If the kernel is updated to a (t,h)-row + w-block program-id mapping (to avoid cross-row blocks), the grid must be updated accordingly or pid0 will exceed the valid (THn_wblocks) range and produce incorrect indexing/masking.
This issue also appears on line 273 of the same file.
INF_I is used as a sentinel for masked-off lanes when reducing t_ws/h_ws/w_lo, but the hardcoded value 999999 can be smaller than valid window starts when T/H/W exceed ~1e6, causing wrong window selection. Use a sentinel guaranteed larger than any possible start (e.g., 1<<30).
INF_I = 999999
aiter/ops/triton/attention/na3d_flash.py:277
The docstring/tests assume rightmost-block correction only runs when the number of corrected queries is small ("TH(W-W_last) should be <= 100_000"), but the implementation will always recompute the entire last W-block when correct_rightmost_block=True. For large T/H this can trigger an unexpectedly expensive fallback path.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The new kernel and correction path depend on mutable Triton autotuner state (best_config) in a way that can be racy and the exact-path mask caching can retain large GPU tensors, both of which warrant careful human review.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
aiter/ops/triton/attention/na3d_flash.py:55
_na3d_sdpa_mask caches full GPU-resident masks (device is part of the cache key). These masks can be large (tiles are chosen up to ~2**25 nq*nk), so lru_cache(maxsize=256) can retain multiple 10s–100s of MB tensors and create GPU memory growth across model runs / long-lived processes.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
There are correctness and operational issues (KW-dependent config validity/pruning, missing runtime gating/bounds, leaked global test state) plus a mismatch between the PR description and the exposed API/features.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (4)
Previously missed (3) — in code that hasn't changed since the last review.
aiter/ops/triton/attention/na3d_flash.py:38
The PR description documents a public na3d_flash_attn(..., correct_rightmost_block=False) option and an _na3d_sdpa_exact(...) reference inside this module, plus tests for the correction path. The current module only exposes na3d_flash_attn(q,k,v,kernel_size) and contains no correction/reference implementation, so either the description needs updating or the missing API/features need to be added to match it. aiter/ops/triton/attention/na3d_flash.py:72
The launcher does not gate execution to the advertised supported architectures (gfx942/gfx950), and it also doesn’t fail fast when KW exceeds what the available BLOCK_Q/BLOCK_KV configs can cover. Without these guards, callers on other GPUs (or with larger KW) may hit compilation/runtime failures or incorrect results (if an invalid config slips through). Add explicit arch and KW bounds checks alongside the existing asserts. aiter/ops/triton/attention/na3d_flash.py:59
This log call eagerly builds an f-string on every invocation. AiterTritonLogger is designed to support lazy %-style formatting via *args (see utils/logger.py), so avoid f-strings here to reduce overhead when INFO logging is disabled.
If the module-level torch.set_default_device("cuda") is removed (to avoid leaking global state), main() will start constructing tensors on CPU by default and the benchmark path will fail. Set the default device inside main() so running this file as a script still works.
def main():
if get_gfx() not in SUPPORTED_GFX:
aiter.logger.warning("na3d_flash unsupported on %s; skipping", get_gfx())
return
This says the masks are cached, but _na3d_sdpa_mask is rebuilt for every unique geometry on each call (as its own docstring explains). Describe the masks as deduplicated instead so the reference's memory/performance behavior is accurate.
GPU-specific correctness and performance require head-matched validation on both supported architectures.
Review effort: Balanced Findings: None
Validated on both supported architectures against this head.
Correctness: gfx950 (MI350X) and gfx942 (MI300X)
Both run the full suite (20/20 passing on each.
Both also pass the CI autotuning gate TRITON_PRINT_AUTOTUNING=1 yields 0 Triton autotuning for function lines, since this PR replaced @triton.autotune with the per-arch JSON config.
Tuning
The two archs genuinely differ. I swept the candidate tiles per KW family on each arch, validating every candidate against the exact reference before timing it:
family
gfx950
gfx942
na3d_flash_small_kw (BLOCK_Q=16, BLOCK_KV=32)
w4 / s2
w4 / s2
na3d_flash_large_kw (BLOCK_Q=32, BLOCK_KV=64)
w4 / s2
w4 / s4
The large-KW divergence is a measured effect: on gfx942 num_stages=4 is 66.5us vs 70.2us for num_stages=2 (5.6%), while on gfx950 the same comparison is within 0.9% and I left it at s2. Each DEFAULT.json now carries values measured on its own architecture.
Performance
bench_na3d_flash.py, bf16, vs the grouped-SDPA path the eager processor falls back to; times include the wrapper's v.contiguous() staging copy, since the benchmark reproduces the decoder's fused-QKV layout:
shape (B×T×H×W, NH=4/8/32, HD=64)
kernel
gfx950 triton / sdpa
gfx942 triton / sdpa
1×18×32×48
3,7,7
1249 / 9946 us (8.0×)
2228 / 10071 us (4.5×)
1×36×64×96
3,5,5
1910 / 22153 us (11.6×)
3448 / 25565 us (7.4×)
1×40×96×96
3,5,5
3319 / 34811 us (10.5×)
5419 / 37428 us (6.9×)
1×16×80×32
11,11,11
1153 / 3345 us (2.9×)
2010 / 4157 us (2.1×)
1×79×192×192
11,11,11
99143 / 178837 us (1.8×)
152070 / 290311 us (1.9×)
1×4×8×128
3,5,27
40 / 363 us (9.1×)
75 / 399 us (5.3×)
@vgokhale all review comments have now been addressed. The remaining change request is outdated and has been addressed and resolved with the per-arch JSON configs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Video diffusion models such as LTX-2.5 use 3D neighborhood attention in
the VAE diffusion decoder: each token at grid position (t, h, w) attends
only to its (KT x KH x KW) neighbors rather than the full sequence. This
PR adds a BF16 MFMA flash-style kernel for that operation targeting gfx942
and gfx950, plus a reference, tests and benchmarks.
Use case example
LTX-2.5 Diffusion decoder attention processor in xDiT inference framework.
Technical Details
Six new files:
aiter/ops/triton/_triton_kernels/attention/na3d_flash.py_na3d_flash_fwd: flash attention inner kernel.aiter/ops/triton/attention/na3d_flash.pyna3d_flash_attn(q, k, v, kernel_size)-public launcher. Input/output shape: (B, T, H, W, NH, HD) bfloat16,
Q pre-scaled by head_dim**-0.5.
Tuned configs provided for gfx950/gfx942 for ltx-2.5 model default shapes.
aiter/op_tests/triton_tests/attention/test_na3d_flash.py_na3d_sdpa_exact(q, k, v, kernel_size, w_offset=0)- Reference method. Exact per-queryneighborhood SDPA. Groups queries by shared relative-window geometry and
calls
F.scaled_dot_product_attentionper group with a cached additive mask.With w_offset=0 and full q/k/v this is equivalent to a per-query FP32
reference but runs on the GPU. With w_offset>0, q is a W sub-slice and
k/v are the full tensors; neighborhood bounds use global W coordinates.
test_na3d_flash: 9 shapes (5 LTX-2.5 default + 5 edge cases); referenceis
_na3d_sdpa_exact, atol=5e-2test_na3d_sdpa_exact_vs_ref: 6 small shapes; cross-checks_na3d_sdpa_exactagainst the FP32 per-query loop
op_tests/op_benchmarks/triton/bench_na3d_flash.pyPerformance benchmark for 3D neighborhood flash attention (na3d_flash) that compares against
_na3d_sdpa_exactreference method fromaiter/op_tests/triton_tests/attention/test_na3d_flash.py.Measures TFLOPS and TB/s per shape
aiter/ops/triton/configs/gfx950/triton/attention/na3d_flash/DEFAULT.json: Tuned config for gfx950aiter/ops/triton/configs/gfx942/triton/attention/na3d_flash/DEFAULT.json: Tuned config for gfx942Test Plan
Run pytest on gfx950:
Test Result
All tests pass on gfx950 (MI350X) in ~30 s.