Repository navigation
Conversation
…8w8 GEMM - The peeled K_ITERS - 2 step read the K_ITERS - 1 b0/a0/b1 LDS tiles with no vmcnt wait + barrier pairing. - Waves 4..7 lag one barrier, so waves 0..3 could read rows/cols waves 4..7 had not finished DMA-ing into LDS. - Symptom: intermittent 16-64 row (or column) bands per tile off by one K tile, max rel err ~0.15, any M. - Replace the barrier after the final a_next1 load with wait_barrier(N_LDS_STEPS_A); perf is unchanged. - Add op_tests/test_flydsl_gemm_a8w8_8wave.py, which checks every one of --repeat launches per shape/kernel.
…ather_kv_b_proj - gather_gemm_8wave.py has the 8-wave GEMM's tail: a plain barrier after the final a_next1 load. - Its SAFE_VMCNT loop wait can leave the K_ITERS - 1 b1 loads (and at BLOCK_M >= 256 the last a0 step) in flight. - Waves then read those b1 / a0 rows before any vmcnt wait of the wave that loaded them covers the LDS DMA. - Not reproduced on gfx950: 0 bad of 30-100 launches per config, also with an HBM hog and with fault injection. - Same fix as the GEMM: wait_barrier(N_LDS_STEPS_A) replaces that barrier; one s_waitcnt in the ISA, perf unchanged. - Extend op_tests/test_flydsl_gemm_a8w8_8wave.py with a repeated-launch gather_kv_b_proj check and its own table.
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
One backend per PR: PR title tags & labels: |
There was a problem hiding this comment.
🔵 Needs a closer look
The remaining B0 synchronization gap needs correction and human review of gfx950 correctness and performance evidence.
1 open finding
What changed in this PR
This PR targets intermittent last-K-tile corruption in FlyDSL’s gfx950 8-wave GEMM and fused gather projection kernels.
Changes:
- Adds wait-before-barrier synchronization to both pipeline tails.
- Adds repeated-launch correctness checks and performance summaries.
| File | Description |
|---|---|
| op_tests/test_flydsl_gemm_a8w8_8wave.py | Adds repeated-launch checks for both kernels. |
| aiter/ops/flydsl/kernels/gemm_a8w8_8wave.py | Adds a final-prefetch wait and ordering comments. |
| aiter/ops/flydsl/kernels/gather_gemm_8wave.py | Adds the corresponding gather pipeline wait. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- mfma.call ends in s_barrier: waves 0..3 pass c10's only after waves 4..7 ran the tail vmcnt(N_LDS_STEPS_A). - So b_next0 is read after both wave groups drained the K_ITERS - 1 b0/a0/b1 loads; the drain needs no move. - gather_kv_b_proj: only the last a0 load (N_LDS_STEPS_A >= N_LDS_STEPS_B) is at risk; b1 is read after vmcnt(0). - Comment-only: final ISA is byte-identical for the 256x256/128x256 GEMM and two gather_kv_b_proj variants.
|
This also explains the intermittent |
rasmith
left a comment
There was a problem hiding this comment.
Looks good to me for the most part, but someone with more AITER experience should probably approve, or maybe a second set of eyes should also approve.
| # K_ITERS - 1 tile here or c10 / c11 read stale A1 data. | ||
| a1_g2s.load(a_next1, (K_ITERS - 1) * BLOCK_K) | ||
| rocdl.s_barrier() | ||
| # Every wave loads a slice of each LDS tile, and waves 4..7 run one |
There was a problem hiding this comment.
Can we simplify these comments (this one and the one below) so they are more straightforward to read?
There was a problem hiding this comment.
Agreed. This is moot now: I'm closing this PR because #6368 already fixes the race on main (details in my PR comment).
| run_perftest, | ||
| ) | ||
|
|
||
| torch.set_default_device("cuda") |
There was a problem hiding this comment.
Do we also need torch.accelerator.set_device_index("cuda")? Not sure, just asking.
There was a problem hiding this comment.
Not needed: torch.set_default_device("cuda") already uses the current device. This is moot now anyway, because I'm closing this PR (details in my PR comment).
|
The barrier fix does seem to make sense to me. |
| ] | ||
|
|
||
|
|
||
| def run_torch(xq, wq, x_scale, w_scale, dtype=dtypes.bf16): |
There was a problem hiding this comment.
Could we call this ref_gemm and the one below ref_gather? It seems thats what these function are doing, the run_torch providing the reference gemm impl and the other one providing the reference gather impl.
There was a problem hiding this comment.
Agreed, those are better names. This is moot now: I'm closing this PR because #6368 already fixes the race on main (details in my PR comment).
|
fix already in main which pr 6368. |
|
Thanks @solinzby1, @rasmith and @AndreasKaratzas. @solinzby1 is right: #6368 already fixes this on main. Its I checked with back-to-back launches on all 300 gfx950 tuned 8-wave rows (about 1.2M calls each):
The gather_kv_b_proj race does not reproduce on main either (0 bad results in 840k calls). So I'm closing this PR. Thanks for the reviews. |

Motivation
The FlyDSL 8-wave a8w8 bpreshuffle GEMM (
flydsl_bpreshuffle_8w_*, gfx950) intermittently returns wrong results: whole 16–64 row (or column) bands of an output tile are off by one K tile (max relative error ~0.15). The same input gives different results on repeated launches. It hits every tile/swizzle/wave variant and is not tied to M: about 1% of calls fail at mid M, 50–90% at M=32768. 291 tuned rows onmainuse these kernels (Kimi-K3, GLM-5.2, GLM-5.3-flash, Qwen3.5).Technical Details
a_next1load withwait_barrier(N_LDS_STEPS_A)(one extras_waitcntin the ISA). The mxfp8 8-wave kernel already does the equivalent.gather_kv_b_proj8-wave (gather_gemm_8wave.py): its tighter main-loop wait (SAFE_VMCNT) can still leave the last K_ITERS-1 a0 load in flight whenN_LDS_STEPS_A >= N_LDS_STEPS_B(e.g. BLOCK_M = 256), and waves 0–3 read it before the loading waves' next wait. It did not reproduce on gfx950, but it gets the same fix. Its b1 tile was always read aftervmcnt(0).s_barrierat the end of everymfma.call. With it,b_next0is read only after both wave groups have executed the new wait.op_tests/test_flydsl_gemm_a8w8_8wave.pychecks every one of--repeatlaunches per shape (into a NaN-filled output, against an fp32 reference) for both kernels, with one summary table each.Test Plan
python op_tests/test_flydsl_gemm_a8w8_8wave.py(both kernels, 20 launches per shape).python -m pytest op_tests/test_flydsl_gather_kv_b_proj.py.run_perftest, fixed and unfixed interleaved, 3 rounds.Test Result
MI355X (gfx950), ROCm 7.2.
main(up to 19 of 20 launches wrong) and passes with the fix.test_flydsl_gather_kv_b_proj.pypasses (100 tests).Submission Checklist
🤖 Generated with Claude Code