Skip to content

[Kernel][FA] Support paged FP8 Flash attention with asymmetric K/V head dim on gfx950 - #1066

Merged
coderfeli merged 59 commits into
ROCm:mainfrom
sammysun0711:upstream/fa-paged-fp8-asymmetric-v
Sep 14, 2026
Merged

coderfeli merged 59 commits into
ROCm:mainfrom
sammysun0711:upstream/fa-paged-fp8-asymmetric-v

Conversation

@sammysun0711

@sammysun0711 sammysun0711 commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Based on #1065.
FlyDSL FA cached prefill currently cannot consume MiMO-V2.5-Pro physical FP8 Q/K192-V128 pages through the public FlyDSL flash-attention API. Padding V to 192 wastes cache memory and bandwidth.

This PR aims to add gfx950 causal packed-varlen FP8 flash attention for page 16/64/1024 vectorized-5D KV and page 1 linear3d KV with Q/K D192 and independent V/output D128 or D192.

Technical Details

  • Use native FP8 MFMA for both QKᵀ and P×V, with FP32 accumulation and BF16 output. Support D128/V128, D192/V128, and D192/V192 directly from vectorized page-64 caches, without cache unpacking or padding V128 to V192.
  • Process two 64-token pages per iteration, overlapping K/V prefetch, LDS staging, and attention computation. Use eight prefetch slots for D128 and six for D192, with vectorized page-ID loads for even-page tables and independently bounded loads for odd/single-page cases.
  • Reduce LDS overhead for D192/V192 through compact K storage, segmented 128+64 V staging, and XOR-swizzled V addressing, allowing the six-slot pipeline to fit within the LDS budget.
  • Reduce softmax overhead by sharing a balanced maximum-reduction tree and one half-wave exchange across each page pair. Retain lazy rescaling and FP8 probability headroom to reduce accumulator rescaling while preserving small probability contributions.

Performance (MI350X/gfx950)

Performance evaluation result with random per-tensor OCP E4M3 inputs, preallocated output, 10 warmups, 30 iters.

Here are performance evaluation results on MI350X (gfx950)
B=1, Hq=16, Hkv=1, Page size=64

Q / KV QK / V Dense ms / TFLOP/s Paged ms / TFLOP/s Paged/dense throughput
4K / 8K D128/V128 0.1634 / 1261.4 0.1583 / 1302.7 103.2727%
4K / 8K D192/V128 0.1958 / 1315.9 0.1907 / 1351.7 102.7168%
4K / 8K D192/V192 0.2853 / 1083.9 0.2697 / 1146.7 105.7994%
16K / 32K D128/V128 1.9870 / 1660.1 1.9140 / 1723.4 103.8152%
16K / 32K D192/V128 2.4513 / 1682.1 2.4013 / 1717.1 102.0815%
16K / 32K D192/V192 3.4994 / 1413.9 3.1610 / 1565.3 110.7072%
32K / 64K D128/V128 7.9717 / 1655.1 7.6582 / 1722.9 104.0941%
32K / 64K D192/V128 9.9334 / 1660.3 9.6941 / 1701.3 102.4680%
32K / 64K D192/V192 13.9294 / 1420.8 12.6117 / 1569.3 110.4483%
64K / 128K D128/V128 32.8131 / 1608.4 32.0217 / 1648.2 102.4715%
64K / 128K D192/V128 39.9067 / 1653.1 39.2168 / 1682.2 101.7590%
64K / 128K D192/V192 55.3884 / 1429.3 50.4983 / 1567.7 109.6837%

B=1-32, Hq=16, Hkv=1, Page size=64, Q/KV=4K/8K

B QK / V Dense ms Paged ms Paged/dense throughput
1 D128/V128 0.1632 0.1577 103.4736%
1 D192/V128 0.1983 0.1907 103.9848%
1 D192/V192 0.2852 0.2694 105.8345%
2 D128/V128 0.2769 0.2694 102.7690%
2 D192/V128 0.3324 0.3274 101.5398%
2 D192/V192 0.4761 0.4316 110.3202%
4 D128/V128 0.5363 0.5231 102.5198%
4 D192/V128 0.6541 0.6480 100.9538%
4 D192/V192 0.9315 0.8434 110.4437%
8 D128/V128 1.0656 1.0369 102.7660%
8 D192/V128 1.3135 1.2991 101.1116%
8 D192/V192 1.8744 1.7337 108.1172%
16 D128/V128 2.1487 2.0842 103.0948%
16 D192/V128 2.6405 2.6060 101.3247%
16 D192/V192 3.7449 3.4632 108.1325%
32 D128/V128 4.3059 4.2110 102.2537%
32 D192/V128 5.3251 5.2420 101.5842%
32 D192/V192 7.5228 6.9631 108.0387%

Hq=16, Hkv=1, causal Q/KV=1:2, FP8, linear3d KV with page size 1

B Q / KV QK / V AITER mha batch prefill page 1 (ms) FlyDSL paged attention page 1 (ms) FlyDSL vs ATIER Speedup
1 4K / 8K D128/V128 0.2729 0.1881 1.451×
8 4K / 8K D128/V128 2.0371 1.2043 1.691×
64 4K / 8K D128/V128 16.8645 9.8558 1.711×
1 4K / 8K D192/V128 0.6325 0.2323 2.723×
1 64K / 128K D128/V128 61.6248 36.4841 1.689×
1 64K / 128K D192/V128 155.6515 45.6946 3.406×
1 64K / 128K D192/V192 155.2055 59.3703 2.614×
  • Q: number of query tokens processed in this prefill chunk.
  • KV: total key/value context length attended by those query tokens, including the cached prefix and current query chunk.
  • B: Batch Size
  • Hq: Query heads
  • Hkv: KV heads

Test Plan

python -m pytest -q tests/kernels/test_flash_attn_fwd.py

Test Result

platform linux -- Python 3.10.12, pytest-9.0.3, pluggy-1.6.0
rootdir: /root/workspace/mimo-opt/flydsl-upstream-prs/pr3-fa-paged-fp8/tests
configfile: pytest.ini
plugins: hypothesis-6.150.2, anyio-4.14.2
collected 1051 items                                                                                                                                                                                                                                                             

tests/kernels/test_flash_attn_fwd.py ..................................................................................................................................................................................................................................... [ 21%]
.......................................................................................................................................................................................................................................................................... [ 47%]
.......................................................................................................................................................................................................................................................................... [ 72%]
.......................................................................................................................................................................................................................................................................... [ 97%]
........................                                                                                                                                                                                                                                                   [100%]

================================================================================================================================ warnings summary ================================================================================================================================
kernels/test_flash_attn_fwd.py::test_paged_fp8_page64_side_stream_copies[ragged]
kernels/test_flash_attn_fwd.py::test_paged_fp8_page64_side_stream_copies[ragged]
  /opt/venv/lib/python3.10/site-packages/Cython/Distutils/old_build_ext.py:15: DeprecationWarning: dep_util is Deprecated. Use functions from setuptools instead.
    from distutils.dep_util import newer, newer_group

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================================================================================================== 1051 passed, 2 warnings in 248.97s (0:04:08) =================================================================================================================

Accuracy verification with pytorch reference:

Path QK / V Maximum absolute error Maximum mean error Minimum row cosine
dense D128/V128 0.003052 0.000375 0.999374
paged D128/V128 0.003052 0.000375 0.999374
dense D192/V128 0.002930 0.000375 0.999317
paged D192/V128 0.002930 0.000375 0.999317
dense D192/V192 0.002930 0.000376 0.999450
paged D192/V192 0.002930 0.000376 0.999450

Submission Checklist

Copilot AI lite review requested due to automatic review settings August 24, 2026 10:19

Copilot AI 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.

Pull request overview

Adds gfx950-only support for paged FP8 flash-attention with asymmetric K/V head dimensions (Q/K=192, V/O=128 or 192) through the existing flydsl_flash_attn_func paged-KV API, avoiding padding V to 192 and reducing cache/bandwidth waste.

Changes:

  • Extend paged flash-attn dispatch/validation to allow FP8 packed-varlen vectorized-KV with independent value_head_dim, producing native-width BF16 output.
  • Generalize gfx950 dualwave FP8 traits/context/utilities to handle paged+vectorized KV and asymmetric output strides.
  • Add correctness and addressing-robustness tests for the new paged FP8 asymmetric-V path (including a >4GiB cache-offset case).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
tests/kernels/test_flash_attn_fwd.py Adds gfx950 paged-FP8 asymmetric-V correctness test and a large-shape >4GiB cache-offset validation.
kernels/attention/flash_attn_utils.py Extends gfx950 dualwave FP8 trait/context/helpers for paged vectorized KV and asymmetric output width/stride.
kernels/attention/flash_attn_interface.py Hooks paged FP8 path into the public API, validates shapes/scales, and allocates native-width BF16 output.
kernels/attention/flash_attn_fp8_paged_gfx950.py New gfx950 paged FP8 packed-varlen kernel module specialized for Q/K=192 and V/O=128/192.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/kernels/test_flash_attn_fwd.py Outdated
@sammysun0711
sammysun0711 force-pushed the upstream/fa-paged-fp8-asymmetric-v branch 2 times, most recently from b22846f to 32dac38 Compare August 26, 2026 15:08
@coderfeli
coderfeli requested review from jhinpan and yanguahe August 27, 2026 09:31

@jhinpan jhinpan left a comment

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.

COMMENT — no blocking defect found in what I can verify offline, but I cannot run the device suite this change depends on, so I am not approving around that gap.

Prior review items

  • Copilot, tests/kernels/test_flash_attn_fwd.py:4413 (≈10 GiB K/V cache can OOM under large_shape) — fixed. At this head, test_paged_fp8_d192_cache_offsets_above_4gib computes cache_bytes = num_pages * (key_page_bytes + value_page_bytes), calls torch.cuda.empty_cache(), reads torch.cuda.mem_get_info() and skips unless cache_bytes + 2 GiB is free. I checked the arithmetic: high_page = ceil(2**32 / 8192) = 524288, num_pages = 524289, cache_bytes = 524289 * 20480 ≈ 10.7 GB, so the guard threshold is ≈12.7 GB — consistent with the reply. The in-test assert high_page * key_page_bytes >= 2**32 / assert high_page * value_page_bytes >= 2**32 still hold exactly at that page count, so the >4 GiB property the test exists to prove is not weakened by the guard.

No other reviewer items are open.

What I verified at this commit

Environment: no build-fly/, no torch, and rocminfo reports no GPU agents — so pytest tests/kernels/test_flash_attn_fwd.py cannot run here at all.

What I did run / check:

  • python3 -m py_compile on all four changed files — clean.
  • python3 scripts/check_repo.py — agent-docs check OK (871 symbols, 16 skills). The typed-arithmetic check aborts with git merge-base HEAD origin/main returning 128 because this is a single-commit shallow checkout; that is an environment limitation, not a finding. I substituted a grep of the added lines for the spellings it rejects (maximumf/minimumf/maxsi/minsi/ceildivsi/…) — none present; the new code uses fx.maxnumf.
  • No added line exceeds 120 columns, so the black gate should be clean (black/ruff are not installed here, so this is a proxy, not the real gate).
  • "Dense FP8 D128 unchanged" claim — checked by hand rather than taken on trust. store_final_o now calls global_idx_o instead of global_idx_q; that is equivalent on the dense path because DualwaveFp8KernelContext.__init__ defaults stride_o_n to stride_q_n (flash_attn_utils.py:4426), kernels/attention/flash_attn_fp8_gfx950.py:137 does not pass stride_o_n, and V_HEAD_DIM defaults to head_dim in _make_dualwave_swp_fp8_traits. Same for o_nrec_bytes. smem_d_rpt = ceil(head_dim / d_128b_size) is identical to the old head_dim // d_128b_size at D128, d_chunks/sdrpt_bf are unchanged when value_head_dim is None, and fp8_pv/fp8_pv_direct only lose their value when paged=True. Every other new branch is behind const_expr(PAGED) / const_expr(KV_VECTORIZED). The only dense-path side effect is a changed cache_tag, i.e. a one-time JIT cache miss.
  • LDS budget for the new config — with varlen=True, bn128 is False so num_prefetch_k = 2: kv = 2 * (821040 + 821088) = 68096 B, vt = 2 * 8 * sdrpt_bf * 544 * 2 B = 34816 B (V128) / 52224 B (V192). Totals 102 KB / 118 KB, inside the gfx950 160 KB budget.
  • New LDS index arithmetic — walked the bounds of _stage_vt_dequant_fp8_vectorized and the vectorized load_v: max written element is 13023 and max read is 13024 against VT_BF16_ELEMS = 13056 at V192 (8671/8672 vs 8704 at V128), and the vectorized K reads stay under 12288 B inside the 16640 B K region. No overrun that I can see.
  • Interface validation vs the existing fixtures — the new expected_v_tail = (Hkv, page_size // kvs, value_head_dim, kvs) matches what _vectorize_paged_kv (tests/kernels/test_flash_attn_fwd.py:405-410) produces, and the new seqlen_k / block_table / out assertions are satisfied by the existing paged fixtures (seqlen_k is int32 with numel() == B; o_t = torch.zeros_like(q_t)). So the in-repo BF16 paged tests should not regress on validation grounds.
  • New reference test math — the K permute (0,3,1,2,4) and V permute (0,2,4,1,3) in test_paged_fp8_d192_asymmetric_value_matches_torch correctly invert the aiter 5D layouts, and the bottom-right causal mask matches cross_seqlen=True.

The gap (why COMMENT, not APPROVE)

Everything above is static. The actual correctness of the new 962-line gfx950 schedule — the sigma-permuted V dequantization, the folded logit scale in qk, _lazy_rescale_o_paged, and the page-rebased 64-bit descriptors — is only demonstrated by tests/kernels/test_flash_attn_fwd.py on real gfx950 hardware. I have no GPU here and no built flydsl, so I am relying entirely on the 103-passed run in the PR description. There are also zero check-runs on this head commit (gh api .../check-runs returns total_count: 0), so CI has not covered it either.

Also worth flagging as state, not as a code defect: this PR is mergeable_state: dirty (conflicts with main) and is stacked on the still-open #1065.

Non-blocking notes

Inline below. None of these are correctness bugs as far as I can tell; the first one is the one I would most like to see removed before merge because it reads as an unfinished edit.

Comment thread kernels/attention/flash_attn_utils.py Outdated
Comment thread kernels/attention/flash_attn_utils.py Outdated
Comment thread kernels/attention/flash_attn_utils.py Outdated
Comment thread kernels/attention/flash_attn_utils.py Outdated
Comment thread kernels/attention/flash_attn_interface.py Outdated
Comment thread kernels/attention/flash_attn_interface.py Outdated

@jhinpan jhinpan left a comment

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.

REQUEST_CHANGES after three independent passes (host/API boundaries, kernel/addressing/numerics, and tests/CI). The new paged-FP8 math itself held up on a real MI355X/gfx950: the full attention file passed 103/103, the >4 GiB page-offset test passed, and extra partial-page/GQA/fully-masked/peaked-logit/V128/V192 probes matched an independent PyTorch oracle (worst max abs error 7.8125e-3). Repo and Python-style checks also pass.

Two host-side correctness defects remain: flattened Q/O can overflow the signed-int32 C ABI, and internal contiguous/cast copies are not ordered before an explicit side-stream launch. Both are reproduced inline below. The PR is also still conflicted with current main, where #1020 added the analogous dense-FP8 overflow handling; rebasing alone will not cover this paged early-return path.

Comment thread kernels/attention/flash_attn_interface.py Outdated
Comment thread kernels/attention/flash_attn_interface.py Outdated
@sammysun0711
sammysun0711 force-pushed the upstream/fa-paged-fp8-asymmetric-v branch from 32dac38 to 68f0587 Compare August 31, 2026 13:28
jhinpan
jhinpan previously approved these changes Aug 31, 2026

@jhinpan jhinpan left a comment

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.

APPROVE. Both blocking items from my CHANGES_REQUESTED review at 32dac38 are fixed at 68f0587, the five non-blocking items are fixed as well, and the BF16 paged ABI regression I flagged as an unstated side effect has been deliberately narrowed. One verification gap is stated at the bottom.

Prior items, re-checked at this head

1. Blocking — internal copies race an explicit stream (comment) — fixed. Every wrapper-owned conversion now runs inside with torch.cuda.stream(launch_stream): (kernels/attention/flash_attn_interface.py:702-722): the block_table.to(torch.int32).contiguous().reshape(-1) cast, the torch.empty output allocation, and q/k/v.contiguous(). Nothing device-side is left on the ambient stream between the guards and exe(..., stream=launch_stream) — the only remaining pre-launch host interaction is seqlen_k.reshape(-1).tolist(), which is a blocking D2H and is order-safe by construction. The regression exists and is the right shape: test_paged_fp8_d192_asymmetric_value_matches_torch[side-copies] (tests/kernels/test_flash_attn_fwd.py) builds stride-2 non-contiguous Q/K/V plus an int64 block_table (so all four copies are real), enqueues torch.cuda._sleep(3_000_000_000) on the default stream, asserts not default_done.query() (the side stream did not wait for it), and then asserts rtol=0, atol=0 against a contiguous reference. Under the old code the copies would sit behind that sleep and the kernel would read uninitialized memory, so the test discriminates.

2. Blocking — no signed-int32 dimension guard on the flattened paged-FP8 ABI (comment) — fixed. flash_attn_interface.py:628-636 computes q_flat_elems = q.numel() and out_flat_elems = q_flat_elems // D * value_head_dim and raises NotImplementedError when either is >= _FP8_MAX_FLAT_ELEMS (2**31, line 47), before any build or launch. The shape I reproduced with, [699051, 16, 192] = 2,147,484,672 elements, is 1,024 above the bound and is rejected. Because paged FP8 requires packed varlen (q.dim() == 3, line 494) the numel() // D * value_head_dim derivation is exact for both the allocated and the caller-supplied out, and the guard sits before the out shape check so the derived value is the one that will actually be flattened. K/V stay rank-5 and the comment at lines 715-718 now says why. test_paged_fp8_d192_rejects_flattened_int32_overflow monkeypatches _FP8_MAX_FLAT_ELEMS down to the tiny fixture's size, so CI gets the coverage without a multi-GiB allocation.

3. Three no-op const_expr(PAGED) branches (comment) — fixed. floor_masked_max (flash_attn_utils.py:5389), reduce_sum (5419) and apply_l_rescale (5460) are now single unconditional returns. The branches that genuinely differ (sub_m 5392, rescale_from_tile_max 5453, lazy_rescale_o 5487) are untouched.

4. Inaccurate "reads zeros beyond the page descriptor" comment (comment) — fixed. Both sites now state the layout fact rather than a false zero-fill claim: flash_attn_utils.py:4984-4985 ("Each wave owns two 64-lane octet rows for the D256-shaped LDS tile. The QK reader ignores the four groups beyond physical D192.") and the smem_d_rpt comment at 1955-1957.

5. Inlined duplicate of _vec_k_src_elem (comment) — fixed. flash_attn_utils.py:4990-4996 calls _vec_k_src_elem(traits, d, self.wave_id_uni, self.lane_in_warp, self.kv_head_idx). I diffed the removed inline block against the helper body (lines 814-824): the oct_idx/ni/dg/src_oct/src_elem chain is term-for-term identical, so the refactor is behaviour-preserving.

6. Open-coded sigma bit-shuffle (comment) — fixed. flash_attn_utils.py:5112 is now bf = [bf[_sigma_k_tile_n(i)] for i in range_constexpr(16)]. The helper's extra (ni & ~15) term is zero over i < 16, so the substitution is exact.

7. seqlen_k / out ABI tightening on the pre-existing BF16 paged path (1, 2) — addressed. The strict int32 [B] requirement is now gated behind paged_fp8 (flash_attn_interface.py:615), so an int64 or over-allocated host-only seqlen_k still works on BF16/F16, with test_paged_legacy_dtype_accepts_host_only_seqlen_metadata pinning it. The out contiguity/shape/device checks do still apply to every paged path; as I said last round, that replaces a silent write-into-a-throwaway-copy, so I read it as a fix and am not blocking on it.

What I ran here

  • python3 scripts/check_repo.py — agent-docs passes. typed-arithmetic cannot resolve origin/main in this pinned single-commit checkout, so I reproduced it by hand: parsed the base…head patch for kernels/**, fed the 1,498 added lines to check_typed_arithmetic_usage.scan_source, zero violations.
  • python3 -m py_compile on all four changed files — clean.
  • Structural diff of flash_attn_utils.py / flash_attn_interface.py / flash_attn_fp8_paged_gfx950.py between 32dac38 and this head, to confirm the only semantic changes since my last review are the six items above plus the rebase onto main's rescale_threshold / _P_HEADROOM_LOG2 work. The PR-owned part of that rebase is rescale_threshold=8.0 for the paged FP8 module; paged PV is HIPREC (bf16 P), so the e4m3 headroom reasoning that motivates the dense 6.0/4.0 split does not apply there and 8.0 is consistent with the unscaled m_diff comparison in _lazy_rescale_o_paged.
  • GitHub check runs at this commit: Check Python Code Style and Check C++ Code Style both green.

Verification gap

This environment has no torch and no pytest, so I could not execute the new gfx950 tests, and unlike my last round I could not re-run the two GPU repros. Both fixes are closed above by code inspection plus a reading of the new regressions, not by execution. The test job at this commit is still in_progress; please confirm it goes green before merging — in particular test_paged_fp8_d192_asymmetric_value_matches_torch[side-copies-*], which is the one whose assert not default_done.query() could in principle be flaky if a runner is slow enough to drain a 3e9-cycle sleep during the side-stream launch.

@coderfeli

Copy link
Copy Markdown
Collaborator

@sammysun0711 seems large perf gap . 30-50% loss. have you validated this? not make sense to regression too much

@sammysun0711

sammysun0711 commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor Author

@sammysun0711 seems large perf gap . 30-50% loss. have you validated this? not make sense to regression too much

Thanks for your feedback. I understand the original performance gap refer to fp8 paged attention vs fp8 dense attention kernel with D128/V128 head dim, I can reproduced it on MI350X/gfx950 with random FP8 inputs.

  • For D128/V128 head dim, I optimize the FP8 paged attention pipeline and the host launch path for Q/KV head. The final native D128/V128 paged kernel reaches 90.76%, 92.13%, and 93.23% of the matched dense kernel at Q/KV 4K/8K, 16K/32K, and 64K/128K.

  • For the PR's D192 target, V128 latency improve from 3.81/58.75 ms to 3.14/48.35 ms at 16K/64K, while V192 improves from 9.56/141.56 ms to 5.80/88.12 ms by resolve register spill.

Here are the final performance evaluation results on MI350X (gfx950) with random per-tensor OCP E4M3 inputs, preallocated output, 10 warmups, 30 iters. Dense comparison is available only for D128/V128 because the upstream dense kernel does not support Q/K D192.

D128

Q / KV Configuration Median latency TFLOP/s Paged/dense TFLOPs
4K / 8K D128/V128 paged 0.1825 ms 1,129.7 90.76%
4K / 8K D128 dense 0.1656 ms 1,244.7 reference
16K / 32K D128/V128 paged 2.1998 ms 1,499.5 92.13%
16K / 32K D128 dense 2.0266 ms 1,627.7 reference
64K / 128K D128/V128 paged 34.1832 ms 1,543.9 93.23%
64K / 128K D128 dense 31.8693 ms 1,656.0 reference

D192

Q / KV Configuration Median latency TFLOP/s Paged V128 vs V192 TFLOPs
4K / 8K D192/V128 paged 0.2605 ms 989.4 150.9%
4K / 8K D192/V192 paged 0.4718 ms 655.5 n/a
16K / 32K D192/V128 paged 3.1439 ms 1,311.5 153.7%
16K / 32K D192/V192 paged 5.7995 ms 853.2 n/a
64K / 128K D192/V128 paged 48.3453 ms 1,364.6 151.9%
64K / 128K D192/V192 paged 88.1212 ms 898.4 n/a

Comment thread kernels/attention/flash_attn_fp8_paged_gfx950.py Outdated
Comment thread kernels/attention/flash_attn_fp8_paged_gfx950.py Outdated
Comment thread kernels/attention/flash_attn_fp8_paged_gfx950.py Outdated
Comment thread kernels/attention/flash_attn_utils.py Outdated
@coderfeli

Copy link
Copy Markdown
Collaborator

much better perf now. still need more clean. use flydsl kernel code cleanup to do a deeper clean.

@sammysun0711
sammysun0711 force-pushed the upstream/fa-paged-fp8-asymmetric-v branch from 5130cbc to 840d793 Compare September 3, 2026 23:21
@sammysun0711

sammysun0711 commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor Author

I've further optimized D192/V192 and D128/V128 performance and extend B>1 support as follow:

  • Optimized D192/V192 with segmented H1/H2 native-FP8 P×V inspired by Non-power-of-two (NPOT) head dimension optimization aotriton#66, improving performance by 6.3–13.5%.
  • Pipelined D128/V128 V-cache prefetch through registers, reaching 95.0–96.1% of matched dense FP8 throughput
  • Extended the paired-page D128 BN128 kernel to packed-varlen batches, reducing B=2–32 latency by 25–39% over the generic fallback.
  • Added grouped, longest-first D192 batch scheduling, improving D192/V128 by up to 10.9% and D192/V192 by up to 17.6%.
  • Refactor paged-FP8 control flow and arithmetic helpers, removed unreachable split-K code.

Here are performance evaluation results on MI350X (gfx950)
B=1, Hq=16, Hkv=1, Page size=64

Q / KV D128/V128 paged Dense D128 Paged/dense D192/V128 paged D192/V192 paged V192/V128 throughput
4K / 8K 0.1725 ms / 1,195.2 TFLOP/s 0.1639 ms / 1,257.9 TFLOP/s 95.01% 0.2610 ms / 987.4 TFLOP/s 0.4346 ms / 711.6 TFLOP/s 72.07%
16K / 32K 2.0737 ms / 1,590.7 TFLOP/s 1.9925 ms / 1,655.5 TFLOP/s 96.08% 3.0717 ms / 1,342.3 TFLOP/s 4.9083 ms / 1,008.1 TFLOP/s 75.10%
32K / 64K 8.2959 ms / 1,590.5 TFLOP/s 7.9126 ms / 1,667.5 TFLOP/s 95.38% 12.0487 ms / 1,368.8 TFLOP/s 18.7560 ms / 1,055.2 TFLOP/s 77.09%
64K / 128K 33.5786 ms / 1,571.7 TFLOP/s 32.1621 ms / 1,641.0 TFLOP/s 95.78% 48.1408 ms / 1,370.4 TFLOP/s 76.1674 ms / 1,039.4 TFLOP/s 75.85%

B=1-32, Hq=16, Hkv=1, Page size=64, Q/KV=4K/8K

B D128/V128 paged D128 dense D128 paged/dense D192/V128 paged D192/V192 paged V192/V128 throughput
1 0.1710 ms / 1,205.4 TFLOP/s 0.1601 ms / 1,287.4 TFLOP/s 93.63% 0.2599 ms / 991.5 TFLOP/s 0.4347 ms / 711.4 TFLOP/s 71.75%
2 0.2873 ms / 1,435.3 TFLOP/s 0.2733 ms / 1,508.9 TFLOP/s 95.12% 0.4081 ms / 1,262.9 TFLOP/s 0.6563 ms / 942.5 TFLOP/s 74.63%
4 0.5624 ms / 1,466.4 TFLOP/s 0.5344 ms / 1,543.3 TFLOP/s 95.02% 0.7800 ms / 1,321.7 TFLOP/s 1.2592 ms / 982.4 TFLOP/s 74.33%
8 1.1097 ms / 1,486.3 TFLOP/s 1.0651 ms / 1,548.6 TFLOP/s 95.98% 1.5814 ms / 1,303.8 TFLOP/s 2.5136 ms / 984.3 TFLOP/s 75.49%
16 2.2348 ms / 1,476.1 TFLOP/s 2.1409 ms / 1,540.8 TFLOP/s 95.80% 3.2128 ms / 1,283.5 TFLOP/s 5.0341 ms / 982.9 TFLOP/s 76.58%
32 4.4692 ms / 1,476.2 TFLOP/s 4.3016 ms / 1,533.8 TFLOP/s 96.24% 6.4871 ms / 1,271.3 TFLOP/s 9.9420 ms / 995.4 TFLOP/s 78.30%

@sammysun0711
sammysun0711 force-pushed the upstream/fa-paged-fp8-asymmetric-v branch from 8d51c4e to a547839 Compare September 7, 2026 06:25
@coderfeli
coderfeli requested review from binding7012 and removed request for yanguahe September 7, 2026 13:11
@sammysun0711
sammysun0711 force-pushed the upstream/fa-paged-fp8-asymmetric-v branch 2 times, most recently from 621484f to 787efd7 Compare September 10, 2026 13:18
@sammysun0711

sammysun0711 commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor Author

I rebased on latest main to add with dense attention with D192/V128 case: 826efd0

  • Use native FP8 MFMA for both QKᵀ and P×V, with FP32 accumulation and BF16 output. Support D128/V128, D192/V128, and D192/V192 directly from vectorized page-64 caches, without cache unpacking or padding V128 to V192.
  • Process two 64-token pages per iteration, overlapping K/V prefetch, LDS staging, and attention computation. Use eight prefetch slots for D128 and six for D192, with vectorized page-ID loads for even-page tables and independently bounded loads for odd/single-page cases.
  • Reduce LDS overhead for D192/V192 through compact K storage, segmented 128+64 V staging, and XOR-swizzled V addressing, allowing the six-slot pipeline to fit within the LDS budget.
  • Reduce softmax overhead by sharing a balanced maximum-reduction tree and one half-wave exchange across each page pair. Retain lazy rescaling and FP8 probability headroom to reduce accumulator rescaling while preserving small probability contributions.

Here are performance evaluation results on MI350X (gfx950)
B=1, Hq=16, Hkv=1, Page size=64

Q / KV QK / V Dense ms / TFLOP/s Paged ms / TFLOP/s Paged/dense throughput
4K / 8K D128/V128 0.1634 / 1261.4 0.1583 / 1302.7 103.2727%
4K / 8K D192/V128 0.1958 / 1315.9 0.1907 / 1351.7 102.7168%
4K / 8K D192/V192 0.2853 / 1083.9 0.2697 / 1146.7 105.7994%
16K / 32K D128/V128 1.9870 / 1660.1 1.9140 / 1723.4 103.8152%
16K / 32K D192/V128 2.4513 / 1682.1 2.4013 / 1717.1 102.0815%
16K / 32K D192/V192 3.4994 / 1413.9 3.1610 / 1565.3 110.7072%
32K / 64K D128/V128 7.9717 / 1655.1 7.6582 / 1722.9 104.0941%
32K / 64K D192/V128 9.9334 / 1660.3 9.6941 / 1701.3 102.4680%
32K / 64K D192/V192 13.9294 / 1420.8 12.6117 / 1569.3 110.4483%
64K / 128K D128/V128 32.8131 / 1608.4 32.0217 / 1648.2 102.4715%
64K / 128K D192/V128 39.9067 / 1653.1 39.2168 / 1682.2 101.7590%
64K / 128K D192/V192 55.3884 / 1429.3 50.4983 / 1567.7 109.6837%

B=1-32, Hq=16, Hkv=1, Page size=64, Q/KV=4K/8K

B QK / V Dense ms Paged ms Paged/dense throughput
1 D128/V128 0.1632 0.1577 103.4736%
1 D192/V128 0.1983 0.1907 103.9848%
1 D192/V192 0.2852 0.2694 105.8345%
2 D128/V128 0.2769 0.2694 102.7690%
2 D192/V128 0.3324 0.3274 101.5398%
2 D192/V192 0.4761 0.4316 110.3202%
4 D128/V128 0.5363 0.5231 102.5198%
4 D192/V128 0.6541 0.6480 100.9538%
4 D192/V192 0.9315 0.8434 110.4437%
8 D128/V128 1.0656 1.0369 102.7660%
8 D192/V128 1.3135 1.2991 101.1116%
8 D192/V192 1.8744 1.7337 108.1172%
16 D128/V128 2.1487 2.0842 103.0948%
16 D192/V128 2.6405 2.6060 101.3247%
16 D192/V192 3.7449 3.4632 108.1325%
32 D128/V128 4.3059 4.2110 102.2537%
32 D192/V128 5.3251 5.2420 101.5842%
32 D192/V192 7.5228 6.9631 108.0387%

Additional odd and large batches

Each request uses Q/KV=4096/8192.

B QK / V Dense ms Paged ms Paged/dense throughput
3 D128/V128 0.4230 0.4006 105.6123%
3 D192/V128 0.5155 0.5131 100.4679%
3 D192/V192 0.7345 0.6598 111.3317%
5 D128/V128 0.6878 0.6610 104.0608%
5 D192/V128 0.8400 0.8299 101.2146%
5 D192/V192 1.1992 1.0630 112.8095%
7 D128/V128 0.9530 0.9294 102.5307%
7 D192/V128 1.1717 1.1535 101.5761%
7 D192/V192 1.6847 1.4987 112.4126%
17 D128/V128 2.3070 2.2388 103.0455%
17 D192/V128 2.8331 2.7963 101.3160%
17 D192/V192 4.0237 3.6997 108.7573%
33 D128/V128 4.4858 4.3491 103.1447%
33 D192/V128 5.5496 5.4526 101.7794%
33 D192/V192 7.8734 7.2072 109.2435%
64 D128/V128 8.7693 8.4935 103.2468%
64 D192/V128 10.8558 10.7857 100.6500%
64 D192/V192 15.2740 14.0560 108.6660%

Accuracy verification

Path QK / V Maximum absolute error Maximum mean error Minimum row cosine
dense D128/V128 0.003052 0.000375 0.999374
paged D128/V128 0.003052 0.000375 0.999374
dense D192/V128 0.002930 0.000375 0.999317
paged D192/V128 0.002930 0.000375 0.999317
dense D192/V192 0.002930 0.000376 0.999450
paged D192/V192 0.002930 0.000376 0.999450

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

like odd number of KV pages -> 24%–46% increase in latency ?

Comment thread kernels/attention/flash_attn_utils.py Outdated
for i in range_constexpr(16):
centered.append(
fx.Float32(
llvm.intr_experimental_constrained_fma(

@binding7012 binding7012 Sep 11, 2026 •

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.

could this inline asm replaced with fx.fma?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed, replace it with fx.fma in: e41c748

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Compact K staging and XOR V addresses to fit six native FP8 K/V slots in LDS.
- Stage future V directly into LDS and load current fragments after softmax.
- Retain the public page-cache ABI and generic odd-page fallback.
- Add ragged multi-KV-head accuracy coverage and pass the expanded attention suite.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Derive native value staging from the supported head dimensions.
- Remove obsolete BF16 V modes, unreferenced staging helpers, and copied trait state.
- Drop redundant dimension guards and unused Q LDS scaffolding.
- Preserve public attention behavior and native instruction schedules.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Replace PR-introduced index wrappers with explicit 64-bit integer values.
- Keep the ring state typed across loop boundaries and retain 32-bit ABI casts.
- Use typed vector conversion and remove redundant scalar wrappers.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Gather vectorized page caches in one independent reference helper.
- Reuse the existing cross-length PyTorch attention oracle.
- Preserve ragged, graph, long-context, and multi-KV-head coverage.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Construct bounded page resources with 64-bit rebasing and make_buffer_tensor.
- Use aligned 16-byte LDS tiles, typed vector loads, and fx.copy stores.
- Replace manual paged control flow and pointer casts with DSL operations.
- Share canonical value-width traits and the register-resident Q loader.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Normalize launch arguments once for cached launches and explicit compilation.
- Preserve the compiled launch fast path and existing argument validation.
- Check explicit compilation against PyTorch for both paged schedules and all supported widths.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Remove stale BF16 V, self-attention, and per-step schedule narration.
- Document aligned LDS views and the remaining intrinsic boundaries.
- Keep cache ABI, synchronization, and numerical rationale.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Simplify the segmented V chunk address calculation and remove redundant segment offsets.
- Keep lane, buffer, and chunk offsets in 16-byte tile units so LLVM shares LDS addresses.
- Reduce generic V192 spilling while preserving wide loads and odd-page accuracy.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Reduce both score tiles through a balanced max tree before one half-wave exchange.
- Preserve causal masking, lazy rescaling, and the native FP8 cache layouts.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Separate below-diagonal pairs from the masked tail to expose QK and max-reduction overlap.
- Carry the same K/V ring state and preserve one cross-wave rendezvous per pair.
- Keep D128 and V192 on their compact single-loop schedule.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Use scalar FP32 FMA for single-sequence D128 and D192 softmax centering.
- Keep packed centering for batched D128 and all dense and generic paths.
- Localize instruction selection while preserving probability headroom scaling.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Enable batch-fast paired-page ordering for the two-request D192/V128 configuration.
- Preserve head-fast ordering in the generic fallback and validate packed-varlen group launches.
- Cover ragged multiple-query-block batches with shuffled pages on a non-default stream.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Bound future FP8 scores with the query L1 norm and the OCP E4M3 finite range.
- Select a max-free loop only when every lane proves lazy rescaling unnecessary; retain the original fallback and cache ABI.
- Initialize the first pair without infinite-seed fast math and keep proof comparisons strict.
- Add ragged, multi-KV-head, mixed-wave, and rescaling regressions; validate 332 tests and 45 of 48 dense-parity comparisons.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Use batch-fast grid-X ordering for paired D128 at B=2/3/5 while preserving other dispatch policies and the native cache ABI.
- Expand ragged, multi-KV-head, bounded/fallback, mixed-wave, and side-stream-copy regression coverage.
- Isolate the stream concurrency test from HIP hardware-queue sharing without changing benchmark priority or assertions.
- Validate all 48 matched comparisons at or above dense throughput, with 359 tests passing and four existing skips.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Share probability packing and active query-block ordering between paged schedules.
- Remove no-op segmented V load-step wrappers while preserving all loads, waits, and rescaling order.
- Retain scheduler-sensitive address expressions and vector construction; nine checked ISA files remain byte-identical.
- Validate the complete paged subset with 124 passing tests and four existing skips.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Centralize paired-batch interleave selection and reuse the validated page count.
- Separate reusable numerical checks from pytest entry points and parameterize duplicate ragged cases.
- Add 18 paired-policy cases without changing numerical assertions or stream concurrency checks.
- Verify dispatch equivalence across 3840 shape, batch, and head combinations.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Trim duplicated stream and phase-shift narration while retaining the barrier and cache-layout rationale.
- Clarify asymmetric V shapes, packed probability order, and on-demand segmented V reads.
- Document the intentionally separate tail address expression that avoids added spills.
- Verify that the executable AST is unchanged; only comments and docstrings differ.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Use generic_load and generic_store for the four typed, 16-byte-aligned paged FP8 LDS access sites.
- Remove the temporary register tensor used solely to issue the V LDS store while preserving bounded global loads and all synchronization.
- Retain the exact qualified prototype implementation: nine ISA files are byte-identical and all 48 comparisons remain above dense throughput.
- Recheck the integrated attention suite with 377 passing tests and four existing skips.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Replace the staggered odd-page fallback with one bounds-safe paired-page pipeline and remove obsolete helpers, scheduling traits, and dispatch options.
- Use fx.gemm with MFMA_Scale, typed LLVM memory primitives, causal selects, and BF16 conversions; replace paged inline assembly with standard constrained-FMA and arithmetic-fence intrinsics.
- Preserve the native cache ABI, stream behavior, and strict query-bound proof while adding odd-page, multi-KV-head, eager/lazy, and no-inline-assembly regressions..

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Support native physical page sizes 1, 16, 64, and 1024 with independent QK/V widths and bounds-safe cache addressing.
- Replace page-1 byte gathers with vector loads and an exact wave transpose; specialize page-16 K/V staging and reuse D128 page translations.
- Use bounded cache descriptors for small pools while retaining the validated 64-bit fallback for larger caches.
- Preserve page-64/page-1024 ISA and qualify accuracy, stream/graph behavior, large offsets, and the complete performance matrix.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Use DPP byte transposes and width-specific LDS word placement for native page-1 attention.
- Reuse small-page K/V translations and bounded cache resources while preserving the 64-bit fallback.
- Shorten page-16 fragment lifetimes and stage V192 segments with uniform scalar K offsets.
- Simplify unused launch plumbing and supported V-width traits while retaining official FlyDSL APIs.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
- Honor packed cu-seqlens for every batch, including B=1, and validate launch metadata, head counts and supported public options.
- Bound per-request page-table lookups and clear inactive V bytes, preserve full-packet fast path.
- Normalize strided cumulative metadata and single-value descales, preserve graph replay and native page-1/16/64/1024 cache layouts.
- Use public fx.fma with explicit nearest-even rounding and load the scalar V descale through fx.generic_load at the paged epilogue.
- Remove unused paged debug counters and obsolete B=1/varlen plumbing; preserve lazy rescaling, non-FP8 debugging and dense split-K handling.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
@sammysun0711
sammysun0711 force-pushed the upstream/fa-paged-fp8-asymmetric-v branch 2 times, most recently from 913775a to 08ac7cb Compare September 14, 2026 01:47
- Centralize KV layouts and simplify prefix/tail handling.
- Stream V fragments for a common D192/V128 page schedule.
- Deduplicate tests while preserving boundary and fallback coverage.

Signed-off-by: Xiake Sun <xiake.sun@amd.com>
Signed-off-by: Xiake Sun <xiake.sun@amd.com>
@sammysun0711

sammysun0711 commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor Author

like odd number of KV pages -> 24%–46% increase in latency ?

Flydsl page attention kernel now supports page size 1 with linear kv layout, 16/64/1024 with vectorized 5d kv layout.
For page size 16/64/1024 with vectorized 5d kv, the latency gap is minimum.

For page size 1: I compared that both FlyDSL paged attention with AITER mha batch prefill with page size 1 vs 16, we can see 10+% latency. With page size 1. A 64-token KV tile spans 64 physical pages; with page size 16, it spans only four. Page 1 therefore needs more page-table lookups, address calculations, and data rearrangement to feed the matrix instructions.

QK / V FlyDSL page-1 latency penalty AITER page-1 latency penalty
D128/V128 +13.1–18.0% +12.9–16.3%
D192/V192 +14.5–17.8% +12.4–13.1%, B=1 only

But flydsl paged attention is still faster than aiter mha with same shape and page size setting.
For example, at B=1, Q/KV=4K/8K, D128/V128 (K=1024):

Backend Page 1 Page 16 Page-1 penalty
FlyDSL 0.1866 ms 0.1635 ms +14.1%
AITER CK 0.2739 ms 0.2392 ms +14.5%

Comment thread .github/workflows/flydsl.yaml
@coderfeli
coderfeli merged commit 1137497 into ROCm:main Sep 14, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants