CUDA: XOR swizzle flash attn K,V smem fp16 tiles - #25635
Conversation
Bug Report (for PR #25635)Title: CUDA illegal memory access during warmup on Ampere (RTX 3060 Ti, CC 8.6) Environment:
Model: Qwen-AgentWorld/Qwen3.5-35B-A3B (hybrid architecture: recurrent Gated DeltaNet + SWA layers, GGUF Q8_0) Reproduction: llama-server -m <hybrid_model.gguf> --ctx-size 131072 --flash-attn on \
--spec-type draft-mtp --spec-draft-n-max 4 -b 16384 -ub 256 \
--cache-type-k q8_0 --cache-type-v q8_0Symptom: Server crashes during warmup ("warming up the model with an empty run") with the following errors: (with Analysis:
Scope:
|
Thank you for reporting the issue. I will try to repro at my end and fix it |
0fe9eeb to
de97e22
Compare
|
@naamfung can you please check if the issue is resolved with the latest change on your system? |
| @@ -66,7 +67,7 @@ static constexpr __host__ __device__ fattn_mma_config ggml_cuda_fattn_mma_get_co | |||
| GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 32, 128, 2, 32, 96, 64, 64, 2, true); | |||
| GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 64, 128, 2, 32, 96, 64, 64, 2, true); | |||
|
|
|||
| GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 8, 64, 4, 64, 128, 128, 128, 2, true); | |||
| GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 8, 128, 2, 64, 128, 128, 128, 2, true); | |||
There was a problem hiding this comment.
What is the perf impact of this change in isolation?
There was a problem hiding this comment.
Collected ncu traces on different config swizzle+occupancy_update , swizzle_only, master, master+occupancy_update to isolate the impact of the change. Main idea for the change was to increase occupancy and was seeing some regression
Ncu values for Qwen3.6_35B collected on RTX6000-PRO
Decode:
| cell | config | swizzle | block | occ% | bc_ld | bc_st | dur |
|---|---|---|---|---|---|---|---|
| 1 | 64,4 | off | 64 | 4.20 | 1141 | 370 | 162 us |
| 2 | 128,2 | off | 128 | 8.30 | 478 | 376 | 161 us |
| 3 | 64,4 | on | 64 | 4.20 | 396 | 188 | 168 us |
| 4 | 128,2 | on | 128 | 8.30 | 376 | 385 | 163 us |
Prefill:
| cell | config | swizzle | block | occ% | bc_ld | bc_st | dur |
|---|---|---|---|---|---|---|---|
| 1 | 64,4 | off | 128 | 15.9 | 27,362 | 26,478 | 4.33 ms |
| 2 | 128,2 | off | 128 | 15.9 | 27,433 | 26,566 | 4.34 ms |
| 3 | 64,4 | on | 128 | 14.9 | 1,29,524 | 17,390 | 4.01 ms |
| 4 | 128,2 | on | 128 | 14.9 | 1,29,963 | 17,654 | 4.02 ms |
on DGX Spark:
Decode:
| cell | config | swizzle | block | occ% | bc_ld | bc_st | dur |
|---|---|---|---|---|---|---|---|
| 1 | 64,4 | off | 64 | 4.17 | 764 | 91 | 0.76 ms |
| 2 | 128,2 | off | 128 | 8.33 | 152 | 122 | 0.75 ms |
| 3 | 64,4 | on | 64 | 4.17 | 99 | 48 | 0.63 ms |
| 4 | 128,2 | on | 128 | 8.33 | 107 | 96 | 0.63 ms |
Prefill:
| cell | config | swizzle | block | occ% | bc_ld | bc_st | dur |
|---|---|---|---|---|---|---|---|
| 1 | 64,4 | off | 128 | 16.63 | 10722 | 8422 | 30.43 ms |
| 2 | 128,2 | off | 128 | 16.63 | 10903 | 7359 | 29.35 ms |
| 3 | 64,4 | on | 128 | 15.29 | 26097 | 11759 | 26.49 ms |
| 4 | 128,2 | on | 128 | 15 | 27460 | 10403 | 27.36 ms |
There was a problem hiding this comment.
At least on DGX Spark, it seems (128,2) is worse than (64,4).
Also, bank conflicts are high with this change during prefill. Is this expected?
There was a problem hiding this comment.
At least on DGX Spark, it seems (128,2) is worse than (64,4).
The change is made only for decode config, for prefill it is most probably noise.
Also, bank conflicts are high with this change during prefill. Is this expected?
it is for op_ld and op_st those values are negligible in end to end run. we should be looking at op_ldgsts(cp.async) which is the swizzle change touching for K/V fill(There is no excessive wavefront on K read and V read). Somehow in ncu i don't see that value.
Collected wavefront data on DGX Spark with CTK 13.3(JFYI i was not able to find the ldgsts bank conflict field so had to look into SASS for which took help from Claude to export the SASS and count the "L1 Wavefronts Shared, L1 Wavefronts Shared Ideal, and L1 Wavefronts Shared Excessive") and below are the observations :
Decode:
| cell | config | swizzle | cp.async wf | cp.async ideal | xideal |
|---|---|---|---|---|---|
| 1 | 64,4 | off | 2.8M | 1.1M | 2.62x |
| 2 | 128,2 | off | 2.8M | 1.1M | 2.61x |
| 3 | 64,4 | on | 1.1M | 1.1M | 1.00x |
| 4 | 128,2 | on | 1.1M | 1.1M | 1.03x |
Prefill:
| cell | config | swizzle | cp.async wf | cp.async ideal | xideal |
|---|---|---|---|---|---|
| 1 | 64,4 | off | 40.6M | 14.7M | 2.76x |
| 2 | 128,2 | off | 40.6M | 14.7M | 2.76x |
| 3 | 64,4 | on | 15.0M | 14.4M | 1.04x |
| 4 | 128,2 | on | 15.0M | 14.4M | 1.04x |
There was a problem hiding this comment.
According to these numbers, please also change the config for ncols == 16 and apply the same changes for the configs with DKQ == 192 && DV == 128.
| for (int kv : { 4096, 8192, 16384,32768, 65536, }) { | ||
| for (int hs : { 64, 128, 256, }) { | ||
| for (int nr : { 1, 4, 8, }) { | ||
| for (int nb : { 1, 4096, }) { | ||
| test_cases.emplace_back(new test_flash_attn_ext(hs, hs, 8, {nr, 1}, kv, nb, true, false, 0, 0, GGML_PREC_F32, GGML_TYPE_F16, GGML_TYPE_F16)); | ||
| } |
There was a problem hiding this comment.
You should reduce the number of tests. This will take too long to run.
There was a problem hiding this comment.
Seems these are perf tests, not eval. If ppl want, they can filter with -p . But we can also reduce them 🤷
|
Can you do a perf sweep for different depth values? |
|
Sorry, I just saw the message. I'll merge and test it right away. Please wait. |
|
8119/8119 tests passed |
|
Startup is working without errors now, but no performance improvement is visible. Maybe the variation is just normal fluctuation? :) It's likely that further optimization potential is limited under the Ampere architecture with small tiles. |
Thank you for confirming the fix is working. |
The build has GGML_CUDA_FA_ALL_QUANTS=ON enabled, but on Ampere with small tiles, the performance gain may fall within statistical noise. |
|
Thanks for the fix. The 64-bit generic pointer commit (de97e22) successfully Environment:
Results (10 repetitions, mean +/- std):
The pp2048 delta is within statistical noise (95% CIs fully overlap), but the This directionally matches the -0.37% tg128 regression you reported for Potential cause (speculative):
The correctness fix is real and appreciated, but on this GPU + model |
| // XOR swizzle for K/V SMEM tiles to avoid bank conflicts without row padding (Turing+ only). | ||
| // Stride must be a power-of-two >= 32 half2 columns,otherwise we keep +4 row padding. |
There was a problem hiding this comment.
Swizzling should be re-usable for GEMMS in mma.cuh, what are the steps needed to achieve/generalize this?
| for (int kv : { 4096, 8192, 16384,32768, 65536, }) { | ||
| for (int hs : { 64, 128, 256, }) { | ||
| for (int nr : { 1, 4, 8, }) { | ||
| for (int nb : { 1, 4096, }) { | ||
| test_cases.emplace_back(new test_flash_attn_ext(hs, hs, 8, {nr, 1}, kv, nb, true, false, 0, 0, GGML_PREC_F32, GGML_TYPE_F16, GGML_TYPE_F16)); | ||
| } |
There was a problem hiding this comment.
Seems these are perf tests, not eval. If ppl want, they can filter with -p . But we can also reduce them 🤷
Hmm you seem to be running both CPU offloading of some sort (16 GB weights on 8 GB GPU) + on a private fork. Can you:
|
I tried the second "Execute the |
|
Please wait.. |
|
Thanks for the pointer on GGML_CUDA_FA_ALL_QUANTS. I've rerun the benchmarks Environment:
Results (10 repetitions, mean +/- std):
The pp2048 improvement is statistically significant (95% CIs of the mean do Earlier I reported a -6.31% tg128 regression, but that was measured on a The 64-bit generic pointer fix (de97e22) also resolves the illegal-memory- |
Performance sweep DGX Spark
Performance sweep RTX6000-PRO
I am collecting sweep on other hardware but facing high run to run variance. Will update here once i have perf sweep on other hardware . |
de97e22 to
4093970
Compare
|
I see decent perf improvement with LLAMA3.1-8B_Q4_K_M and GPT-OSS-120B on DGX SPark
|
Performance sweep Ampere
Performance sweep Turing
|
| @@ -1471,6 +1484,8 @@ static __device__ __forceinline__ void flash_attn_ext_f16_process_tile( | |||
| const bool thread_should_write = T_C_KQ::J == 8 || T_C_KQ::get_j(threadIdx.x & 2) < 8; | |||
| #endif // defined(TURING_MMA_AVAILABLE) | |||
|
|
|||
| __syncthreads(); | |||
There was a problem hiding this comment.
Are these __syncThreads needed only for swizzle path? If yes, can we make these conditional?
There was a problem hiding this comment.
Yes, they are required for swizzled path due to "half2 * tile_K = Q_in_reg ? tile_Q" and padding is removed. Making this conditional.
There was a problem hiding this comment.
Padding-removal alone should not change pipeline staging & data re-use though. Please clarify
c1fb1ce to
0166491
Compare
Signed-off-by: ynankani <ynankani@nvidia.com>
Signed-off-by: ynankani <ynankani@nvidia.com>
Signed-off-by: ynankani <ynankani@nvidia.com>
Signed-off-by: ynankani <ynankani@nvidia.com>
Signed-off-by: ynankani <ynankani@nvidia.com>
Signed-off-by: ynankani <ynankani@nvidia.com>
Signed-off-by: ynankani <ynankani@nvidia.com>
Signed-off-by: ynankani <ynankani@nvidia.com>
Signed-off-by: ynankani <ynankani@nvidia.com>
Signed-off-by: ynankani <ynankani@nvidia.com>
987a04c to
088d425
Compare
Performance sweep DGX Spark for JenniSD/DeepSeek-V2-Lite-Chat-Q4_K_M-GGUF
|
Performance sweep RTX-PRO-Blackwell 6000 for JenniSD/DeepSeek-V2-Lite-Chat-Q4_K_M-GGUF
|
|
Thanks Johannes for the review. We can relax the condition to be more inclusive of other shapes. I will do a follow PR for refactoring and if find a way possible to get similar or more returns from memory pointer based swizzling as it will be cleaner approach. |
|
As discussed offsite, due to deadline constraints on the NVIDIA side I am fine with merging this PR as-is as long as there is a refactor in a follow-up. This is the performance I'm measuring when doing a sweep over batch size: Performance
Swizzling is faster on average but there are some cases where it is not. For now I think it is OK to merge like this but for the refactor please make whether or not to use swizzling part of the kernel configuration so that it can be controlled in a fine-grained way. |
* CUDA: XOR swizzle flash attn K,V smem fp16 tiles Signed-off-by: ynankani <ynankani@nvidia.com> * Fix use 64bit generic pointer instead of 32bit shared pointer Signed-off-by: ynankani <ynankani@nvidia.com> * fix shared memory race in FA on DGX Spark * Handle corener case Signed-off-by: ynankani <ynankani@nvidia.com> * Add swizzle test cases and gate sync for swizzled path only Signed-off-by: ynankani <ynankani@nvidia.com> * gate CUDA PTX Signed-off-by: ynankani <ynankani@nvidia.com> * offset calculation specific for swizzle branch Signed-off-by: ynankani <ynankani@nvidia.com> * Reafctor code Signed-off-by: ynankani <ynankani@nvidia.com> * Refactor FA swizzle ldmatrix if/else into helpers (K row/col, V offset) Signed-off-by: ynankani <ynankani@nvidia.com> * rebase and update test case args Signed-off-by: ynankani <ynankani@nvidia.com> * Allow swizzle for non-pow2 shapes, for which nbatch_2%32==0 Signed-off-by: ynankani <ynankani@nvidia.com> --------- Signed-off-by: ynankani <ynankani@nvidia.com>
KV restore batching (ggml-org#27991), kv-cells seq-scan early stop (ggml-org#28011), MOE fusion to specdec + multi-token (ggml-org#27621), mm_ids_helper templated fast path (ggml-org#27978), qwen4exp recurrent state rollback (ggml-org#28123), n_layer_nextn load order (ggml-org#28159), FA K/V XOR-swizzle smem tiles (ggml-org#25635), --lazy-mode -lzm (ggml-org#27837/ggml-org#27969). TQ3/TurboQuant stack and vitriol-* integration auto-merged clean; no conflicts. Experiment E1 of mining-experiment-master-plan-2026-09-01.
* CUDA: XOR swizzle flash attn K,V smem fp16 tiles Signed-off-by: ynankani <ynankani@nvidia.com> * Fix use 64bit generic pointer instead of 32bit shared pointer Signed-off-by: ynankani <ynankani@nvidia.com> * fix shared memory race in FA on DGX Spark * Handle corener case Signed-off-by: ynankani <ynankani@nvidia.com> * Add swizzle test cases and gate sync for swizzled path only Signed-off-by: ynankani <ynankani@nvidia.com> * gate CUDA PTX Signed-off-by: ynankani <ynankani@nvidia.com> * offset calculation specific for swizzle branch Signed-off-by: ynankani <ynankani@nvidia.com> * Reafctor code Signed-off-by: ynankani <ynankani@nvidia.com> * Refactor FA swizzle ldmatrix if/else into helpers (K row/col, V offset) Signed-off-by: ynankani <ynankani@nvidia.com> * rebase and update test case args Signed-off-by: ynankani <ynankani@nvidia.com> * Allow swizzle for non-pow2 shapes, for which nbatch_2%32==0 Signed-off-by: ynankani <ynankani@nvidia.com> --------- Signed-off-by: ynankani <ynankani@nvidia.com>
* CUDA: XOR swizzle flash attn K,V smem fp16 tiles Signed-off-by: ynankani <ynankani@nvidia.com> * Fix use 64bit generic pointer instead of 32bit shared pointer Signed-off-by: ynankani <ynankani@nvidia.com> * fix shared memory race in FA on DGX Spark * Handle corener case Signed-off-by: ynankani <ynankani@nvidia.com> * Add swizzle test cases and gate sync for swizzled path only Signed-off-by: ynankani <ynankani@nvidia.com> * gate CUDA PTX Signed-off-by: ynankani <ynankani@nvidia.com> * offset calculation specific for swizzle branch Signed-off-by: ynankani <ynankani@nvidia.com> * Reafctor code Signed-off-by: ynankani <ynankani@nvidia.com> * Refactor FA swizzle ldmatrix if/else into helpers (K row/col, V offset) Signed-off-by: ynankani <ynankani@nvidia.com> * rebase and update test case args Signed-off-by: ynankani <ynankani@nvidia.com> * Allow swizzle for non-pow2 shapes, for which nbatch_2%32==0 Signed-off-by: ynankani <ynankani@nvidia.com> --------- Signed-off-by: ynankani <ynankani@nvidia.com>
Signed-off-by: ynankani <ynankani@nvidia.com>
Overview
This PR adds XOR swizzling for K/V shared-memory tiles in the CUDA flash-attention fp16 MMA kernel (fattn-mma-f16.cuh), replacing row padding (+4) as the primary fix for shared-memory bank conflicts on cp.async stores and ldmatrix loads. On Turing+ (Ampere/Ada/Blackwell), when the K/V tile stride is a power-of-two ≥ 32, the kernel uses a pow2 stride with per-row XOR address remapping instead of padded row stride.
Additional information
Followed resource : https://lubits.ch/flash/Part-4
This change helped in improving perf for very high context, collected data for 65K depth
Performance DGX -SPARK
Performance RTX6000-PRO-BLACKWELL
Requirements