Skip to content

[ROCm] Faster transformer inference on RDNA2 (gfx103x): dot2 attention, optional CK DPP GEMM - #1269

Open
zhaob1n wants to merge 4 commits into
lightvector:masterfrom
zhaob1n:rocm-rdna2-transformer
Open

zhaob1n wants to merge 4 commits into
lightvector:masterfrom
zhaob1n:rocm-rdna2-transformer

Conversation

@zhaob1n

@zhaob1n zhaob1n commented Oct 18, 2026

Copy link
Copy Markdown

Summary

On RDNA2 (gfx103x: no matrix cores, no CK FMHA kernels) the ROCm backend ran transformer nets with the generic online-softmax attention kernel (~45-50% of transformer forward time on a gfx1030) and hipBLAS for every projection. This PR adds an RDNA2-specific attention kernel built on v_dot2_f32_f16, an optional Composable Kernel DPP GEMM path for the FP16 token GEMMs, and a few ROCm-only bandwidth fixes. The attention and GEMM changes are on by default only on gfx103x; other AMD archs get only the ROCm vectorized elementwise kernels (see the table below), and CUDA runs the same kernels as before.

Changes

  • dot2 attention (cpp/neuralnet/rocmkernels.h/.hip, new). Each lane owns whole query rows (online softmax stays lane-local), and K/V reach the ALU as scalar (SGPR) operands of v_dot2_f32_f16 loaded through the scalar cache, since LDS/VMEM broadcast tops out well below the dot2 rate. A prep kernel compacts on-board keys (masked boards cost nothing), interleaves V in key pairs, pre-scales K by log2(e)/sqrt(d) and applies RoPE (table or learnable recompute), replacing Step 3's RoPE launch. Small batches split the keys over up to 4 wave groups merged through LDS. Head dims 32 and 64, GQA by head grouping.
    • rocmUseDot2Attention (auto/true/false). auto = gfx1030-gfx1036 only. Other archs with v_dot2_f32_f16 only with = true. Where CK FMHA is attempted, it still runs first; dot2 replaces the plain kernel as its fallback only on the archs where dot2 is enabled.
  • Optional CK DPP GEMM (cpp/neuralnet/rocmckgemm.h/.hip, new; CMake section "Optional CK DPP GEMM (RDNA2)"). Built only for the gfx103x archs of the build when CK headers are present and a cached configure probe compiles it (same pattern as the CK FMHA probe); otherwise KATAGO_ROCM_HAS_CK_GEMM=0 and nothing changes. Instances are CK's gridwise DPP GEMM with C column-major in CK's terms (16-byte C stores) and Set/Add variants.
    • Used for FP16 transformer projections and NHWC 1x1 convs. Per GEMM shape, the first calls rotate through hipBLAS and the CK instances, timed in place with events (polled, never waited on), and the fastest is kept.
    • With CK enabled and dot2 attention as the only consumer, Q/K/V are written as one interleaved [tokens, q|k|v] buffer (the existing combined-QKV layout, now also on ROCm), and FFN linear1/gate as [tokens, linear1|gate] with SwiGLU in place; hipBLAS produces the same layouts via strided-batched GEMMs with an output leading dimension.
    • rocmUseCkGemm (auto/true/false), auto = on where built.
  • ROCm-only bandwidth kernels (cudaandrocmhelpers.inc, under KATAGO_GPU_HIP): 16-byte vectorized FP16 scale/bias/activation, masked residual add, SwiGLU (bit-identical arithmetic), wave-per-row RMSNorm (same per-element math, different sum-of-squares order), and an LDS-tiled NCHW/NHWC transpose (the existing ROCm-only transpose, which the MIOpen NCHW fallback made hot).
  • Docs: Compiling.md, gtp_example.cfg, analysis_example.cfg, CK FMHA README.

Scope by architecture

Default behavior change
CUDA None. Shared-code changes are plumbing only (MatMulLayer::apply(..., inputLd), applySharedInputStridedMatMuls(..., outputLd), shouldCombineQKV restructured with the CUDA condition unchanged). Not compiled on CUDA here (no CUDA toolchain); please build-check.
gfx103x (RDNA2) dot2 attention; CK DPP GEMM if CK headers are installed; ROCm vectorized kernels.
other AMD Only the ROCm vectorized kernels (bit-identical except RMSNorm sum order). dot2 attention opt-in. CK GEMM is never built for them.

CI configs (gfx942;gfx1100, Linux ROCm and Windows TheRock) do not build CK GEMM; a local build for gfx942;gfx1100 compiles cleanly.

Measurements

RX 6800 XT (gfx1030), ROCm 7.2.4 (CK from the same release), 19x19, FP16, katago benchmark -v 800 -t 8,16,24,32 -n 10, best visits/s across the thread sweep (including the extra server-thread and batch-size configurations benchmark tries on its own):

Model master (d91ea85) this PR speedup
b10c384 (kata1-tf2-b10c384-s2941M) 1041 2489 2.39x
b10c512h8nbt3tflrs-fson-silu-rsnh 584 1069 1.83x
b11c768h12nbt3tflrs-fson-silu 307 527 1.72x

Correctness

On gfx1030: runnnlayertests pass; testbackendreference PASSED on tf3-b10c512, tf2-b10c384, b28c512nbt; testgpuerror (19x19, 9x9, and requireMaxBoardSize=true) within limits for the transformer nets above and two convnets; a standalone harness checks dot2 attention against an FP32 reference with logits scaled up to 1000x. FP16 error is somewhat higher than master's, still far from the limits. testgpuerror -quick -boardsize 19, closest margins (99% / max; the max column is a single worst position and noisy):

Model master this PR dot2 attention only CK GEMM only
b10c384 0.133x / 0.097x 0.158x / 0.243x 0.156x / 0.174x 0.139x / 0.136x
b10c512h8 0.123x / 0.083x 0.127x / 0.127x 0.122x / 0.109x 0.132x / 0.101x
b11c768h12 0.169x / 0.393x 0.178x / 0.233x 0.181x / 0.135x 0.164x / 0.170x

Both parts add a little: dot2 attention multiplies V by FP16 softmax weights (p <= 256, so 11 significant bits) where the plain kernel keeps them in FP32, and CK's accumulate rounds to FP16 before adding (below).

Caveats / questions

  • Tested only on gfx1030. Head dim 64 and GQA paths are implemented but only head dim 32 without GQA ran through real nets (head dim 64 checked in a standalone harness).
  • In-place GEMM tuning: until a shape's choice settles (a few calls), outputs come from different but equivalent kernels (same FP32-accumulated product, different summation order), and the final choice is not reproducible across runs. Happy to move this to warmup or a cache if preferred.
  • CK accumulate (residual adds fused into outProj/linear2 with no mask, and nbt post convs) rounds the product to FP16 before the add, like the existing masked-board residual path, unlike hipBLAS beta=1. On full testgpuerror with requireMaxBoardSize=true (b10c512h8), winrate error mean was 0.0531% with CK accumulate excluded and 0.0552% with it. Without it, exact-board inference (benchmarknn -require-exact-nnlen, b10c512h8) is 13% / 6% / 5% / 4% slower at batch 4 / 8 / 16 / 32.
  • VRAM: with CK GEMM on, token-projection weights are kept in both layouts (in-major for hipBLAS, out-major for CK).
  • Not tested: Windows/TheRock with gfx103x CK GEMM.
  • Can split into (1) dot2 attention, (2) CK GEMM + interleaved layouts, (3) ROCm vectorized kernels if that is easier to review.

- dot2 attention (rocmkernels.h/.hip): K/V as scalar-cache SGPR operands of
  v_dot2_f32_f16, lazily rescaled online softmax, prep kernel that compacts
  on-board keys and fuses RoPE; key splits for small batches.
  Config: rocmUseDot2Attention (auto = archs with v_dot2_f32_f16).
- Optional CK DPP GEMM (rocmckgemm.h/.hip, gfx103x builds with CK installed)
  for FP16 transformer projections and 1x1 convs, chosen per GEMM shape by
  timing against hipBLAS on first use. Config: rocmUseCkGemm.
- Vectorized FP16 NHWC scale/bias/activation and masked residual add,
  wave-per-row RMSNorm, LDS-tiled batched transpose.

gfx1030 (RX 6800 XT) benchmarknn, nnEval/s at batch 8 / 32:
tf3-b10c512 443->741 / 535->946, tf3-b11c768 249->386 / 286->449,
tf2-b10c384 787->1532 / 1018->2025. testgpuerror within limits.
- With the CK GEMM kernels enabled, Q/K/V (when dot2 attention consumes them)
  and the FFN linear1/gate projections write one buffer interleaved per token,
  so each can run as a single CK GEMM; hipBLAS writes the same layout through
  strided-batched GEMMs with an output leading dimension. SwiGLU then works in
  place at that row stride and linear2 reads it there.
- CK instances use the gridwise DPP GEMM directly with C column-major in CK's
  terms (16-byte C stores) and an Add variant, so accumulating GEMMs (nbt post
  convs, outProj/linear2 with requireMaxBoardSize) can use CK too.
- CK vs hipBLAS (and which CK instance) is chosen per GEMM shape by timing the
  first real calls with events instead of isolated back-to-back runs, which
  ranked candidates differently from the model's interleaved calls.
- Vectorized (16-byte) SwiGLU kernel.

gfx1030, tf3-b10c512 benchmarknn -require-exact-nnlen ms/batch:
b4 7.50->6.2, b8 11.0->9.9, b16 18.6->17.9, b32 34.2->33.0.
- rocmUseDot2Attention=auto now only enables dot2 attention on gfx103x
  (rocmDot2AttentionAutoArch), the archs it was measured on; other archs
  with v_dot2_f32_f16 need rocmUseDot2Attention=true.
- The vectorized scale/bias, masked residual add, wave-per-row RMSNorm and
  SwiGLU kernels are ROCm-only, so the CUDA backend runs exactly the kernels
  it did before.
- CK GEMM tuning no longer blocks: it keeps rotating candidates until the
  last sample's event has completed (hipEventQuery) instead of
  hipEventSynchronize. Documented the transient/non-reproducible choice and
  the FP16 rounding of CK's accumulate.
- CK GEMM configure probe: compiles all targeted gfx103x archs with the
  build-type flags, signature covers build type/flags/archs, and reports
  missing CK headers.
- Log messages name the dot2 kernel when CK fused attention falls back to it.
- Docs/config: dot2 auto scope, gfx1030-only measurements.
…elow the reference

When every score of a row's first key pair (of the whole row, or of a key-split
chunk) is more than 8 below the initial reference 0, the rescale moved the
reference down with alpha = exp2(-shift) = inf, and 0 * inf turned the still
empty l and acc into NaN. Seen as "Got nonfinite for policy sum" with
b11c768h12nbt3tflrs-fson-silu. Clamp alpha to 1: it only ever exceeds 1 when
nothing has been accumulated yet.
@zhaob1n
zhaob1n marked this pull request as ready for review October 21, 2026 15:12

This branch has not been deployed

No deployments
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.

1 participant