Skip to content

new MHA v4 entrypoint ; hosts a spectrum of quantized & sparse attention kernels - #4627

Open
jcaraban wants to merge 62 commits into
mainfrom
mha_v4
Open

new MHA v4 entrypoint ; hosts a spectrum of quantized & sparse attention kernels#4627
jcaraban wants to merge 62 commits into
mainfrom
mha_v4

Conversation

@jcaraban

@jcaraban jcaraban commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Motivation

⚠️ These MHA kernels are mainly tested with Diffusion Inference workloads.

Introduce an extensible, inference-only MHA v4 entrypoint for mixed low-precision ASM attention kernels, without overwhelming the existing aiter.ops.mha module. In the future we can consider merging into the common entrypoint, but these kernels are best kept independent as they evolve.

Q: why v4? A: IMO this is the closest thing to FAv4 on cdna3/4, also doing 1) conditional-softmax-rescaling, 2) exponential-approx/emu, 3) optimized pipelines for valu/mfma/loads utilization. Though I'm open for naming suggestions.

Replaces #3495 and #3186

Technical Details

  • Introduces optimized low-precision dense attention for gfx950, with initial gfx942 support.
  • Adds INT8, FP8, MXFP6 E2M3, and MXFP4 E2M1 operand combinations with BF16 output.
  • Includes the binaries, quantization and packing routines, host dispatch, JIT integration, and benchmark coverage.
  • Establishes aiter.ops.mha_v4 as an extensible interface for future architectures, formats, output types, sparse attention, GQA, causal attention, head dimensions, etc
  • Preserves efficient torch.compile execution and distributed overlap by separating Q/K/V preprocessing and safely encapsulating specialized packed layouts.

Test Plan

  • Run focused MHA v4 pytest coverage.
  • Validate eager versus full-graph compiled parity.
  • Exercise allocator churn and downstream tensor consumption.
  • Run the unified 8K benchmark across all kernels.

Test Result

  • 19 focused tests passed.
  • All six raw paths matched eager and compiled execution.
  • gfx942/gfx950 manifest generation passed.
  • The 8K all-kernel benchmark completed successfully.

Current mi355x long-sequence dense ASM kernel throughput, excluding Q/K/V preprocessing:

Q/K V O TFLOPs
INT8 FP8 BF16 2315
FP8 FP8 BF16 3118
mxFP6 FP8 BF16 3430
mxFP4 FP8 BF16 3540
mxFP6 FP4 BF16 3790
mxFP4 FP4 BF16 4000

Submission Checklist

jcaraban and others added 30 commits June 2, 2026 18:25
Commit 25e68ae (i8fp8 post-process fix) inadvertently replaced the
production hsa/gfx942/fmha_v3_fwd/MI300/fwd_hd128_fp8.co. Restore the
original production blob (8f659ed, 31976B) from origin/main; the i8fp8
PR should not touch the fp8 kernel binary.
The gfx950 fwd_hd128_mxfp6 kernel's pre-shifted Region-B K-scale gather
reads each token's E8M0 dword at a +1-byte offset, which runs 1 byte
past the scale tensor on the final (token,head,batch). Re-home `scale`
into a buffer with trailing slack so that read stays mapped (the pad
byte is never consumed by the MFMA op_sel).
Rebuilt from asm/fmha_v3_fwd/mi350/fwd_hd128_bf16_woven.py (diffusion repo
bb95bcf0). Verified: builds, deploys, MAE 0 vs known-good, ~1417 TFLOPS.
quantize_fp6_k_lds_order_triton now always emits the 17408B-per-tile K layout
(16384B chunk-major fp6 data + a 1024B lane-major K-scale tail: Region A
unshifted + Region B pre-shifted), returning a stride-136 k_view. The
fwd_hd128_mxfp6 kernel reads the scale straight from the K-buffer tail
(coalesced buffer_load lds:1), so the separate K-scale global-load stream --
which was stalling -- is gone. Drops the AITER_MXFP6_PACK_KSCALE_TAIL env knob.

Consumers (bench_sage, xDiT attention_backend) use the returned k_view directly
and auto-adapt to the new stride; there are no return_raw callers.

Also updates the deployed fwd_hd128_mxfp6.co to the matching tail-consuming
kernel (tail scale + bf16-MMA QK bias + address-gen hoist) so the packer and
kernel stay coherent.
Replace the 32-iteration tl.static_range linear-search E2M3 encode in
_pack_qk_fp6_kernel with a branchless round-half-even arithmetic encode.
The E2M3 magnitude grid is a minifloat (2 exp bits, 3 mantissa bits,
bias 1): normals (mag>=1) are 2^(exp2-1)*(1+m/8), subnormals (mag<1) are
m/8. So the search collapses to (a) fp32 RNE-round-to-3-mantissa-bits for
the normal range (add 0x7FFFF + kept-LSB, carry into exp) and (b)
round-half-even of mag*8 for the subnormal range.

Bit-identical to the search and the numpy packer (verified across
scales). The pack kernel was compute-bound on the search (~360 GB/s, far
below HBM); num_warps/BLOCK_N tuning was a wash. Result: fp6 pack
0.255ms -> 0.089ms; full mxfp6 e2e quant 0.496 -> 0.315ms, flipping
mxfp6 e2e above fp8 (hq32 sq8192 1183->1462, hq5 sq65536 2537->2727).
Kernel output bit-identical (cos 0.99745/0.99779). Drop the now-dead
GRID constexpr param + call-site arg.
The arithmetic-encode _pack_qk_fp6_kernel is bandwidth/latency-bound on
its permuted per-block gather, so many small single-warp programs hide
the load latency better than fewer wide ones. Measured ~17-19% faster
than 128/default-4 across shapes (bit-exact): e.g. N=382500 (H5 S76500)
0.127->0.104ms, N=262144 (H32 S8192) 0.090->0.073ms.
…slot

Adds the f6f4 attention variant (fp6 Q/K, per-channel fp4 E2M1 V read via
ds_read_b64_tr_b4) as a first-class kernel that coexists with the mainline f6f8
(fp6-QK / fp8-V) build instead of sharing/overwriting one .co slot.

- sage_attention_quant_wrappers: add production sage_quant_mxfp6(..., f6f4=,
  v_fp4_packer=, q_packer=, k_packer=). Computes only the selected V operand (fp8 via
  sage_quant_v_kernel for f6f8, per-channel fp4 via the caller-supplied packer for f6f4).
  Q/K default to the in-tree Triton fp6 packers, but q_packer/k_packer can override
  (e.g. the bench's AITER_MXFP6_PACK swap / numpy fallback).
- bench_sage: use the production sage_quant_mxfp6; keep the host-module (hp) Q/K packers
  (_build_fp6_qk_packer / _build_fp6_k_coalesced_packer via _load_host_fp6_pack, honoring
  AITER_MXFP6_PACK / AITER_MXFP6_QK_TRITON) and pass them as q_packer/k_packer; f6f4 packs
  V via the host Triton packer.
- dispatch (asm_mha_fwd.cu): pick "f6f4bf16" vs "mxfp6bf16" by V dtype (fp4 uint8
  vs fp8); treat f6f4bf16 as mxfp-packed with bf16 output.
- mha_fwd.cu: accept f6f4bf16 in the fp6/descale path + v3 support check, and key
  the kernel cache by (symbol | .co path) so same-symbol/different-.co variants
  don't collide.
- codegen.py: include co_name in the generated config-map key for the same reason.
- fmha_fwd.csv: add the f6f4bf16 row -> fwd_hd128_f6f4.co (the .co is deployed
  separately, not tracked in git).

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread aiter/ops/mha_v4.py
q_scale_mode: AttentionScaleMode,
k_scale_mode: AttentionScaleMode,
v_scale_mode: AttentionScaleMode,
softmax_scale: Optional[float] = None,

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.

⚠️ [ruff] <UP045> reported by reviewdog 🐶
Use X | None for type annotations

Suggested change
softmax_scale: Optional[float] = None,
softmax_scale: float | None = None,

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.

No! This exact change led to e2e regressions when it was applied to aiter.ops.mha recently. It messes with Inductor and no test is capturing the regression yet.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every PR:

  • ✅ Pre-checks (submodule verification, code formatting)
  • ✅ Aiter op tests (gfx942 + gfx950)
  • ✅ Triton tests on MI35X (only when aiter/ops/triton/** or related paths are changed)

Extended tests (opt-in via labels):

Label Tests
ci:triton-300x Run an additional Triton test job on MI300X in PRs; main branch always runs both MI35X and MI300X
ci:sglang SGLang integration tests: DeepSeek-R1-MXFP4 accuracy, Qwen 3.5 accuracy
ci:atom ATOM benchmark: DeepSeek-R1-0528, GPT-OSS-120B
ci:atom_full ATOM accuracy suite for PR and main models from ATOM models_accuracy.json
ci:vllm vLLM benchmark: GPT-OSS-120B, DeepSeek-R1-0528, Kimi-K2.5
ci:all 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 4627 --add-label <label>

Move the fused hd128 Hadamard rotation and MXFP4/MXFP6 packing kernels out of the DeepSeek-v4 rotate-quant module and into the module_fmha_v4_fwd ownership boundary.

Add native at::Tensor declarations and pybind entries for the two preprocessors, compile the new mha_v4_quant.cu source with the existing v4 extension, and expose the wrappers from aiter.ops.mha_v4. Update bench_sage to consume the v4-owned API.

Restore dsv4_rotate_quant.h, dsv4_rotate_quant.cu, rocm_ops.hpp, and aiter.ops.quant to their origin/main state so this PR no longer changes DeepSeek-specific or generic quant surfaces. Also remove the three unrelated a4w4 blockscale tuning rows inherited by the branch.

The relocated kernels preserve the original GPU math and launch geometry. They were checked bit-for-bit against the prior generated DeepSeek binding for FP16 and BF16 inputs across multiple multipliers.

Validation: python -m pytest op_tests/test_mha_v4.py -q (19 passed); Ruff and git diff checks pass; both relocated gfx950 quantizers build and launch successfully.
@jcaraban
jcaraban requested review from amd-junhshen and junhaha666 and removed request for amd-junhshen August 7, 2026 12:35
Make the native fmha_v4_fwd binding a void out-buffer mutator instead of returning the same storage as an apparently fresh tensor. Wrap it in an MHA v4-local torch.library custom op that marks only out as mutated, keeping the generic compile_ops machinery unchanged.

This corrects Inductor's alias and buffer-lifetime model. The previous schema marked every tensor mutable and returned an undeclared alias of out, which produced block-scrambled output in a fully compiled Wan pipeline despite passing standalone numerical checks.

Keep mha_v4_packed's public return contract by returning out from Python after the native launch. Add a schema regression test alongside the existing eager/fullgraph parity coverage.

Validation: 20 focused MHA v4 tests pass; direct v4 FP8 generated a spatially coherent Wan video at 16.94s; focused Ruff and diff checks pass.
Comment thread aiter/ops/mha_v4.py
Comment on lines +4 to +32
from enum import IntEnum
from typing import Optional

import torch
import triton
from torch import Tensor

from aiter import dtypes
from aiter.ops.triton._triton_kernels.quant.sage_attention_quant import (
sage_quant_v_amax_finalize_kernel,
sage_quant_v_amax_partial_kernel,
sage_quant_v_kernel,
)
from aiter.ops.triton.quant.mxfp6_fmha_pack import (
fp6_k_raw_buffer_sizes,
fp6_k_lds_order_views_from_raw,
reorder_fp6_k_lds_order_triton,
)
from aiter.ops.triton.quant.sage_attention_quant_wrappers import (
fp4_v_padded_sequence,
fp4_v_raw_buffer_size,
sage_quant_v_f4f4,
)

from ..jit.core import compile_ops
from ..jit.utils.chip_info import get_gfx


MHA_V4_LOG2E = 1.4426950408889634

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.

⚠️ [ruff] <I001> reported by reviewdog 🐶
Import block is un-sorted or un-formatted

Suggested change
from enum import IntEnum
from typing import Optional
import torch
import triton
from torch import Tensor
from aiter import dtypes
from aiter.ops.triton._triton_kernels.quant.sage_attention_quant import (
sage_quant_v_amax_finalize_kernel,
sage_quant_v_amax_partial_kernel,
sage_quant_v_kernel,
)
from aiter.ops.triton.quant.mxfp6_fmha_pack import (
fp6_k_raw_buffer_sizes,
fp6_k_lds_order_views_from_raw,
reorder_fp6_k_lds_order_triton,
)
from aiter.ops.triton.quant.sage_attention_quant_wrappers import (
fp4_v_padded_sequence,
fp4_v_raw_buffer_size,
sage_quant_v_f4f4,
)
from ..jit.core import compile_ops
from ..jit.utils.chip_info import get_gfx
MHA_V4_LOG2E = 1.4426950408889634
from enum import IntEnum
from typing import Optional
import torch
import triton
from torch import Tensor
from aiter import dtypes
from aiter.ops.triton._triton_kernels.quant.sage_attention_quant import (
sage_quant_v_amax_finalize_kernel,
sage_quant_v_amax_partial_kernel,
sage_quant_v_kernel,
)
from aiter.ops.triton.quant.mxfp6_fmha_pack import (
fp6_k_lds_order_views_from_raw,
fp6_k_raw_buffer_sizes,
reorder_fp6_k_lds_order_triton,
)
from aiter.ops.triton.quant.sage_attention_quant_wrappers import (
fp4_v_padded_sequence,
fp4_v_raw_buffer_size,
sage_quant_v_f4f4,
)
from ..jit.core import compile_ops
from ..jit.utils.chip_info import get_gfx
MHA_V4_LOG2E = 1.4426950408889634

Add fused native K packing and explicit raw-layout helpers to MHA v4, reject incompatible packed layouts, and update the benchmark and tests for the new ABI.
Comment thread aiter/ops/mha_v4.py
q_scale_mode: AttentionScaleMode,
k_scale_mode: AttentionScaleMode,
v_scale_mode: AttentionScaleMode,
softmax_scale: Optional[float] = None,

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.

⚠️ [ruff] <UP045> reported by reviewdog 🐶
Use X | None for type annotations

Suggested change
softmax_scale: Optional[float] = None,
softmax_scale: float | None = None,

Comment thread aiter/ops/mha_v4.py
k_scale_mode: AttentionScaleMode,
v_scale_mode: AttentionScaleMode,
softmax_scale: Optional[float] = None,
out: Optional[Tensor] = None,

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.

⚠️ [ruff] <UP045> reported by reviewdog 🐶
Use X | None for type annotations

Suggested change
out: Optional[Tensor] = None,
out: Tensor | None = None,

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.

⚠️ [ruff] <I001> reported by reviewdog 🐶
Import block is un-sorted or un-formatted

from __future__ import annotations
import argparse
import csv
import glob
import json
import logging
import os
import re
import sys
import tempfile
import time
from dataclasses import dataclass
from typing import Any, Literal
import torch
import triton
import aiter
from aiter.ops.mha import (
flash_attn_func,
)
from aiter.ops.mha_v4 import (
AttentionFormat,
MHA_V4_LOG2E,
mha_v4,
mha_v4_packed,
mxfp4_k_view,
native_fp8_format,
quantize_mxfp4_k,
rotate_activation_mxfp4_quant,
rotate_activation_mxfp6_quant,
scale_modes_for_formats,
)
from aiter.ops.triton._triton_kernels.flash_attn_triton_amd import flash_attn_3
from aiter.ops.triton._triton_kernels.quant.sage_attention_quant import (
sage_quant_v_amax_finalize_kernel,
sage_quant_v_amax_partial_kernel,
sage_quant_v_kernel,
)
from aiter.ops.triton.attention.fav3_sage import (
fav3_sage_func,
fav3_sage_wrapper_func,
get_sage_fwd_configs,
)
from aiter.ops.triton.attention.fav3_sage_attention_mxfp4_wrapper import (
fav3_sage_mxfp4_func,
fav3_sage_mxfp4_wrapper,
get_sage_fwd_configs_mxfp4,
)
from aiter.ops.triton.attention.mha_v3 import _quantize_bshd
from aiter.ops.triton.attention.utils import block_attn_mask_to_ragged_lut
from aiter.ops.triton.quant.mxfp6_fmha_pack import (
reorder_fp6_k_lds_order_triton,
)
from aiter.ops.triton.quant.sage_attention_quant_wrappers import (
create_hadamard_matrix,
sage_quant,
sage_quant_f4f4,
sage_quant_mxfp4,
sage_quant_mxfp6,
sage_quant_v_f4f4,
)
from aiter.test_mha_common import attention_ref, attention_ref_block_sparse
from op_tests.op_benchmarks.triton.utils.benchmark_utils import (
get_caller_name_no_ext,
)
from op_tests.triton_tests.attention.test_fav3_sage import (
check_attention_outputs,
compare_accuracy,
)

Route the F4F4 row through MHA v4's canonical MXFP4 K buffer, enforce the packed-layout contract, and update benchmarks and tests.
Comment thread aiter/ops/mha_v4.py
q_format: AttentionFormat,
k_format: AttentionFormat,
v_format: AttentionFormat,
softmax_scale: Optional[float] = None,

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.

⚠️ [ruff] <UP045> reported by reviewdog 🐶
Use X | None for type annotations

Suggested change
softmax_scale: Optional[float] = None,
softmax_scale: float | None = None,

Comment thread aiter/ops/mha_v4.py
k_format: AttentionFormat,
v_format: AttentionFormat,
softmax_scale: Optional[float] = None,
out: Optional[Tensor] = None,

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.

⚠️ [ruff] <UP045> reported by reviewdog 🐶
Use X | None for type annotations

Suggested change
out: Optional[Tensor] = None,
out: Tensor | None = None,

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.

3 participants