Skip to content

Perf/pr596 linear attention - #603

Draft
donghanbj wants to merge 5 commits into
mainfrom
perf/pr596-linear-attention
Draft

Perf/pr596 linear attention#603
donghanbj wants to merge 5 commits into
mainfrom
perf/pr596-linear-attention

Conversation

@donghanbj

Copy link
Copy Markdown
Contributor

Summary

Related issue or design

Why

What

Test plan

Notes for reviewers

Checklist

  • The change is focused, or links a design/series explaining its scope.
  • Relevant tests were added or updated and the results are documented.
  • User-facing or design documentation was updated when needed.
  • Substantial AI assistance is disclosed, and I reviewed and understand the result.

BoarQing and others added 5 commits July 29, 2026 23:53
Rewrite the gated_delta linear-attention prefill from a 587-deep serial
chunk recurrence (grid=B*Hkv=32 blocks, 25% occupancy, latency-bound) into a
three-pass chunk-parallel form that exploits the affine chunk-state map
S_out = L_c*S_in + b_c, with the dominant [Q;W]@s0 GEMM on the RDNA3
wave-matrix cores:

  Pass 1 (grid = B*Hkv*nchunks): S_in-independent locals
          Uloc=(I-trilT)^-1(beta.*v), W=(I-trilT)^-1(diag(beta.*A)K),
          Ktilde, a_last.
  Pass 2 (grid = B*Hkv, light serial scan): U = Uloc - W*S0,
          S1 = a_last*S0 + Ktilde^T U (fp16-rounded per chunk).
  Pass 3 (grid = B*Hkv*nchunks): O = scale*(A.*(Q*S0) + P_lower*U), with the
          stacked [Q;W]@s0 GEMM via __builtin_amdgcn_wmma_f32_16x16x16_f16_w32.

Numerically equivalent to the serial kernel (same fp32 math, same per-chunk
fp16 rounding); validated cosine=1.0000000 vs an fp64 per-token gated_delta
reference.

Measured (Qwen3.6-35B, gfx1151, seq 18778): kernel 604.8 ms -> 110.0 ms
(5.5x); RGP peak-clock occupancy 25% -> 25-100%, wavefronts 256 -> 150272;
E2E 16k prefill TTFT 33476 ms -> 17580 ms (1.90x). No regression at shorter
sequences (in128 1.51x, in2048 1.77x E2E).

Co-authored-by: Cursor <cursoragent@cursor.com>
… path

Promote the optimized path to the only implementation and drop the bring-up
scaffolding now that it is validated:

  - Delete the original serial chunked kernel (linear_attention_prefill_-
    chunked_kernel) and the scalar Pass-3 variant (la_pf_pass3_out); they are
    superseded by the chunk-parallel + WMMA path.
  - Remove all debug/tuning env gates: HIPDNN_EP_LA_PARALLEL, _WMMA, _PROF,
    _SCANBLK, _P3BLK, and the per-pass hipEvent timing instrumentation.
  - The launcher now always dispatches the three-pass parallel path; on any
    launch/alloc failure it returns nonzero so the runtime falls back to the
    per-token loop (unchanged higher-level safety net). WMMA needs dk,dv
    multiples of 16, so decline otherwise (-> per-token loop).

Behavior for the supported gated_delta config is unchanged: cosine=1.0000000
vs the fp64 reference, kernel 109.6 ms at seq 18778 (5.5x vs the removed
serial kernel).

Note: HIPDNN_EP_LA_PARALLEL still exists for the separate decode-path G-split
(la_par_G) and is untouched here.

Co-authored-by: Cursor <cursoragent@cursor.com>
…buffer

Reduce the chunk-parallel gated_delta prefill's device scratch and fix its
lifetime, on top of the chunk-parallel + WMMA prefill (#589).

- Move the prefill scratch out of a process-static, never-freed g_la_scratch
  into the per-session RuntimeState::la_scratch pool (grow-on-demand, freed in
  hipdnn_ep_state_cleanup), mirroring qmoe_scratch/conv_scratch. The kernel now
  takes a caller-owned scratch + size; the wrapper sizes it via the new
  hip_linear_attention_prefill_scratch_bytes(). Footprint is now bounded to the
  session and released on teardown instead of held for the whole process.

- Store only the per-token rlast scalar (C floats/chunk) instead of the full
  Ktilde = rlast*K tile (C*dk floats/chunk); the scan rebuilds Ktilde from the
  original key tensor. Removes the Kt_g buffer (~1/dk the size) at bit-identical
  values.

Both changes are numerically exact (same fp32 math / per-chunk fp16 rounding);
only op-order-invariant scratch and buffer sizing change. Validated end-to-end
on Qwen3.5-9B (gfx1151): chunk-parallel prefill path exercised (dk=dv=128),
coherent output, no regression.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Vectorize adjacent scan columns and use all pass-3 wavefronts, cutting seq-2048 prefill latency by about 20% with bit-identical output.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown

Thanks for opening a PR!

This project follows LLVM's incremental-development and AI-tool-use
guidance. See CONTRIBUTING.md
for the project workflow.

Before requesting review, please check that:

  1. The change is focused. Substantial work links the relevant issue
    or design discussion.
  2. The PR documents relevant test results and updates affected
    documentation.
  3. If AI tools provided substantial assistance, the description
    explains what was assisted and how it was validated, and commit
    trailers identify the tool. The contributor has reviewed and
    understands the result.

Reviewers are assigned through
CODEOWNERS where ownership
is configured.

@donghanbj

Copy link
Copy Markdown
Contributor Author

/run-test models=all

@amd-yanjunz

amd-yanjunz commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

ROCm OGA CI finished with result: UNSTABLE.

@github-actions

Copy link
Copy Markdown

L2 Accuracy Results (EP vs CPU)

Model Combined L2 Total Elems Skipped NaN/Inf
conv_test_hybrid 4.8668E-07 64 0
GroupQueryAttention_seq256 25.2366 2621440 0
MatMulNBits_o_seq128 259.906 368640 0
QMoE_seq128 34.957 368640 0

Threshold: 0.01 | Run: 3450 - Commit: 3e29e97

@github-actions

Copy link
Copy Markdown

MorphiZen EP Performance Results

Model QPS Session (s) 1st Infer (ms) CPU% Mem (MB)
full_model_seq128 7.51 6.36 365 3 1245
GroupQueryAttention_seq128 4430.63 1.68818 11 6 311
matmul_down_seq128 517.55 2.49 75 3 352

EPContext Export Performance

Model QPS Session (s) 1st Infer (ms) CPU% Mem (MB)
full_model_seq128 7.52 46.32 364 3 15591

EPContext Import Performance

Model QPS Session (s) 1st Infer (ms) CPU% Mem (MB)
full_model_seq128 7.51 9.75 361 3 15760

OGA Benchmark Results

Model Warmup Reps Prompt Len Gen Tokens TTFT (ms) TPS Peak Mem (GB) GPU Mem (GB)
gpt-oss-20b-webgpu-int4-rtn-block-32 1 5 128 128 176.5 80.3 1.33 13.54
Llama-3.1-8B-awq-g128-int4-asym-fp16-onnx-dml 1 5 128 128 265.2 41.2 1.22 6.43

OGA Wheel Smoke (Python benchmark_e2e.py)

Model TTFT (ms) TPS
Llama-3.1-8B-awq-g128-int4-asym-fp16-onnx-dml 195 39.8

Run: 3450 - Commit: 3e29e97

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