SYCL: add oneMKL GEMM flash attention for XMX-accelerated prompt proc… - #25025
Conversation
|
Hi @johnkarlhill, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
|
Adding before and after... both compiled with arch flags to show side-by-side. Compiling without arch flags will degrade performance from these numbers but should still be better than stock. I'll add more models if needed. |
|
@johnkarlhill For user, how to trigger the new code in usage? Thank you! |
|
Tested on dual Intel Arc Pro B60 (Battlemage, 24GB each), oneAPI 2026.0, MKL 2026.0, targeting Short context (pp512 — MKL path NOT active)No regression vs master:
Long context (pp2048 — MKL path active)
Decode speeds (tg128) unchanged across all models (±2%). Full commands# Master
./build-master/bin/llama-bench -m model.gguf -p 512,2048 -n 128 -ngl -1 -fa 1
# PR #25025
./build-pr/bin/llama-bench -m model.gguf -p 512,2048 -n 128 -ngl -1 -fa 1 |
|
Updated arch table:
All of these use underscores ( I incorrectly listed Arc A770 / A750 as acm_g12. "For user, how to trigger the new code in usage?" |
|
I hope this helps. 255H, Arc 140T, 32GB RAM, llama-cli Build options: after PR:
before PR:
build: f728ada (9793) Thank you. However, I am observing intermittent behavior where the most recently entered prompt is not being processed, and the model instead generates a response to the previous prompt. I believe further testing is needed to confirm whether this issue is reproducible and to identify the underlying cause. This behavior may be unrelated to this PR. |
I can reproduce the behavior on Gemma4 models and working on a fix. This behavior does not exist on Qwen models. Multiple folks have tested with a few different Qwen models and can't reproduce this. It seems specific to Gemma4. And a huge THANK YOU for testing this. It is very much appreciated!!! |
|
@johnkarlhill Thank you! |
|
Bug fix The MKL normalize kernel was writing output using a dense head-major layout (head * n_queries * DV), but llama.cpp's flash attention output uses an interleaved layout (query * n_heads + head per row, matching TILE's flash_attn_combine_results). Head 0 row 0 happened to alias at offset 0 in both layouts, so the first layer's first 64 output floats matched TILE. Everything else landed at wrong addresses. One-line fix in mkl_fa_normalize_head. Tested models (all pass multi-turn coherence)
Performance (Intel Arc Pro B70, Battlemage BMG-G21, 32K context, q8_0 KV cache)
Token generation unaffected (±1 t/s, within noise) — MKL only activates for prompt processing (n_kv ≥ 1024 with quantized KV). Updated PR25025 - Qwen3.6-27B-MTP-UD-Q5_K_XL on B70.txt How to test |
a4871d8 to
ce37155
Compare
Removed 7 redundant stream->wait() calls — 3 no-ops covered by the SYCL in-order queue, 3 in a diagnostic block that was already gated behind MKL_FA_DIAG=1 (removed the whole block), and 1 final drain before pool destructors that's unnecessary with in-order semantics. The 4 remaining waits are all required: oneMKL gemm() runs on its own internal queue that does not respect the SYCL in-order queue. Without these barriers, the softmax kernel would read stale KQ data and the accumulate kernel would read stale VKQ_chunk data. TILE uses zero explicit waits because everything is pure SYCL — MKL can't avoid these handshake points. Perf unchanged. 609 (new) vs 606 t/s... within noise. |
- Fix mkl_fa_normalize_head: use interleaved dst layout ((query * n_q_heads + head) * DV) matching TILE's flash_attn_combine_results. Previously used dense head-major layout which wrote head outputs to wrong addresses, corrupting attention for all models except Qwen3.6-27B (where GQA=6 heads were sparse enough to avoid visible overlap). - Remove 7 redundant stream->wait() calls — SYCL in-order queue already serializes pure SYCL kernel dependencies. Retain only the 4 MKL GEMM ↔ SYCL handshake barriers (oneMKL GEMM uses its own internal queue that does not respect SYCL in-order). - Remove unused dst_row_stride, diagnostic clutter, and dead K/V hex dump (fa_diag block in fattn-mkl.cpp). - Add MKL_FA_DISABLE=1 env var for A/B testing. - Add FA-DISP watchdog (MKL_FA_DEBUG=1) and FA-DIAG output fingerprint (MKL_FA_DIAG=1) in fattn.cpp. Tested: Gemma-4-26B, Gemma-4-31B, Qwen3.6-27B, Qwen3.6-35B-A3B Perf (B70/Battlemage, 32K, q8_0 KV): Gemma-4-26B: 1473 t/s MKL vs 746 TILE (1.97x) Qwen3.6-27B: 609 t/s MKL vs 330 TILE (1.85x) Co-Authored-By: Claude Code on DeepSeek-v4-Pro
| static int fa_diag = -1; | ||
| static int fa_diag_count = 0; | ||
| if (fa_diag < 0) { | ||
| const char * e = getenv("MKL_FA_DIAG"); |
There was a problem hiding this comment.
Rename:
MKL_FA_DISABLE to GGML_SYCL_ENABLE_MKL_FA.
MKL_FA_DIAG to GGML_SYCL_MKL_FA_DIAG
Explain them in SYCL.md, refer to chapter: # Environment Variable.
| if (kb == BEST_FATTN_KERNEL_MKL) kname = "MKL"; | ||
| if (kb == BEST_FATTN_KERNEL_TILE) kname = "TILE"; | ||
| if (kb == BEST_FATTN_KERNEL_VEC) kname = "VEC"; | ||
| fprintf(stderr, "[FA-DIAG] #%d %s D=%d n_kv=%lld n_q=%lld " |
There was a problem hiding this comment.
fprintf(stderr, ) is replaced by GGML_LOG_INFO()
| // the same D — helps detect cache-truncation issues. | ||
| static int nkv_debug = -1; | ||
| if (nkv_debug < 0) { | ||
| const char * e = getenv("MKL_FA_DEBUG"); |
There was a problem hiding this comment.
Rename:
MKL_FA_DEBUG to GGML_SYCL_MKL_FA_DEBUG
Explain it in SYCL.md
| const char * e = getenv("MKL_FA_DISABLE"); | ||
| mkl_disable = (e && e[0] == '1') ? 1 : 0; | ||
| } | ||
| if (mkl_disable == 0 && Q->ne[1] >= 128 && K->ne[1] >= 1024 |
There was a problem hiding this comment.
if (mkl_disable == 0 && Q->ne[1] >= 128 && K->ne[1] >= 1024
Add comment to explain how to trigger FA_MKL in usage or llama-cli/server/bench parameters.
…, document in SYCL.md Completed the following: - Rename MKL_FA_DISABLE → GGML_SYCL_ENABLE_MKL_FA (inverted: 0 to disable) - Rename MKL_FA_DEBUG → GGML_SYCL_MKL_FA_DEBUG - Rename MKL_FA_DIAG → GGML_SYCL_MKL_FA_DIAG - Replace fprintf(stderr, ...) / fflush(stderr) with GGML_LOG_INFO() macro - Document all three env vars in docs/backend/SYCL.md under Runtime - Add comment explaining MKL FA activation trigger (flash-attn + quantized KV cache + batch-size >= 1024 + n_kv >= 1024) Resolves review feedback from arthw. Again, thank you!!! Co-Authored-By: Claude Code on DeepSeek-v4-Pro
ce37155 to
5a81c11
Compare
| // MIT license | ||
| // Copyright (C) 2025 Intel Corporation | ||
| // SPDX-License-Identifier: MIT | ||
| // |
There was a problem hiding this comment.
llama.cpp follow the unified copyright definition.
So, no need to declare here.
remove:
//
// MIT license
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Identifier: MIT
//
| | GGML_SYCL_USE_LEVEL_ZERO_API | 1 (default) or 0 | Use Level Zero API for device memory allocation instead of SYCL. Reduces system RAM usage on Intel dGPUs by avoiding DMA-buf/TTM host memory staging. Requires GGML_SYCL_SUPPORT_LEVEL_ZERO_API=ON at build time. SYCL backend always runs on Level Zero running time even if it's set as OFF (The SYCL api will be usage for memory allocation).| | ||
| | GGML_SYCL_DISABLE_DNN | 0 (default) or 1 | Disable running computations through oneDNN and always use oneMKL. | | ||
| | GGML_SYCL_ENABLE_VMM | 0 or 1 (default) | Enable the virtual-memory device pool. | | ||
| | GGML_SYCL_ENABLE_MKL_FA | 1 (default) or 0 | Enable oneMKL GEMM flash attention for XMX-accelerated prompt processing with quantized KV cache. Set to 0 to force the TILE kernel path for A/B testing. Activated at runtime when flash-attn is enabled (`-fa` or `--flash-attn on`), KV cache is quantized (`--cache-type-k/q *_0/*_1`), and KV length ≥ 1024. | |
There was a problem hiding this comment.
User can find the parameter: --cache-type-k in llama-cli.
But how to set for KV length ≥ 1024?
Please provide a detailed method in llama-cli/server for common user.
| #define MKL_TAKE_TIME(t0) auto t0 = std::chrono::steady_clock::now() | ||
| #define MKL_ACCUM(acc, t0) acc += (int64_t)std::chrono::duration_cast \ |
There was a problem hiding this comment.
The two macros are used to debug for perf.
They should be disabled as default.
| stream->wait(); | ||
| try { ev.wait_and_throw(); } catch (sycl::exception & e) { |
There was a problem hiding this comment.
stream->wait() & ev.wait_and_throw() are duplicated wait() code.
Maybe impact side effect.
Remove one of them.
| stream->wait(); | ||
| try { ev.wait_and_throw(); } catch (sycl::exception & e) { |
There was a problem hiding this comment.
same comment as above: remove one.
| const char * e = getenv("GGML_SYCL_ENABLE_MKL_FA"); | ||
| mkl_disable = (e && e[0] == '0') ? 1 : 0; |
There was a problem hiding this comment.
The code can be replaced by existed function, like
ggml_sycl_get_env("GGML_SYCL_USM_SYSTEM", 0);
…ove dup waits, gate perf macros - Replace raw getenv() with ggml_sycl_get_env() in all 4 env-var checks (fattn.cpp: GGML_SYCL_ENABLE_MKL_FA, GGML_SYCL_MKL_FA_DEBUG, GGML_SYCL_MKL_FA_DIAG; fattn-mkl.cpp: GGML_SYCL_MKL_FA_DEBUG) - Remove duplicated stream->wait() before ev.wait_and_throw() in GEMM KQ and GEMM VKQ — ev.wait_and_throw() already waits for completion - Gate MKL_ACCUM macro behind do_print so timing accumulators are no-ops in normal operation - Remove redundant MIT/Intel copyright header from fattn-mkl.cpp - Remove unused #include <cfloat> - Expand SYCL.md MKL FA docs with step-by-step activation trigger and example llama-cli command Again, thank you!!! Co-Authored-By: Claude Code on DeepSeek-v4-Pro
Remove the quantized-only restriction on MKL activation — the MKL kernel converts any non-F16 K/V to F16 via to_fp16_sycl before GEMM, so F16 (default), BF16, and F32 caches all benefit from XMX hardware acceleration. The type restriction was an unnecessary gate. Before (F16/BF16 default cache + FA on at 32K prefill): ~356 t/s (TILE path) After: ~670 t/s (MKL path, matching quantized-cache baseline) Minimal change: two conditions removed, one comment updated in fattn.cpp. No kernel or conversion code changes — the dequant pipeline already covers all types.
Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com>
Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com>
…nv-var one-liners
…/llama.cpp into sycl-mkl-flash-attn
|
@arthw Updated based on your suggestions. Thank you!! |
|
@johnkarlhill |
|
@arthw I think you meant the conflict with the new GGML_SYCL_ENABLE_FUSION env variable, so I added that. Please let me know if there was another conflict I needed to address. |
|
@johnkarlhill Thank you! |
ggml-org#25025) * SYCL: add oneMKL GEMM flash attention for XMX-accelerated prompt processing * fattn-mkl: fix interleaved dst layout in normalize kernel - Fix mkl_fa_normalize_head: use interleaved dst layout ((query * n_q_heads + head) * DV) matching TILE's flash_attn_combine_results. Previously used dense head-major layout which wrote head outputs to wrong addresses, corrupting attention for all models except Qwen3.6-27B (where GQA=6 heads were sparse enough to avoid visible overlap). - Remove 7 redundant stream->wait() calls — SYCL in-order queue already serializes pure SYCL kernel dependencies. Retain only the 4 MKL GEMM ↔ SYCL handshake barriers (oneMKL GEMM uses its own internal queue that does not respect SYCL in-order). - Remove unused dst_row_stride, diagnostic clutter, and dead K/V hex dump (fa_diag block in fattn-mkl.cpp). - Add MKL_FA_DISABLE=1 env var for A/B testing. - Add FA-DISP watchdog (MKL_FA_DEBUG=1) and FA-DIAG output fingerprint (MKL_FA_DIAG=1) in fattn.cpp. Tested: Gemma-4-26B, Gemma-4-31B, Qwen3.6-27B, Qwen3.6-35B-A3B Perf (B70/Battlemage, 32K, q8_0 KV): Gemma-4-26B: 1473 t/s MKL vs 746 TILE (1.97x) Qwen3.6-27B: 609 t/s MKL vs 330 TILE (1.85x) Co-Authored-By: Claude Code on DeepSeek-v4-Pro * Thank you for the review feedback: rename env vars, use GGML_LOG_INFO, document in SYCL.md Completed the following: - Rename MKL_FA_DISABLE → GGML_SYCL_ENABLE_MKL_FA (inverted: 0 to disable) - Rename MKL_FA_DEBUG → GGML_SYCL_MKL_FA_DEBUG - Rename MKL_FA_DIAG → GGML_SYCL_MKL_FA_DIAG - Replace fprintf(stderr, ...) / fflush(stderr) with GGML_LOG_INFO() macro - Document all three env vars in docs/backend/SYCL.md under Runtime - Add comment explaining MKL FA activation trigger (flash-attn + quantized KV cache + batch-size >= 1024 + n_kv >= 1024) Resolves review feedback from arthw. Again, thank you!!! Co-Authored-By: Claude Code on DeepSeek-v4-Pro * Thank you for the review feedback round 2: use ggml_sycl_get_env, remove dup waits, gate perf macros - Replace raw getenv() with ggml_sycl_get_env() in all 4 env-var checks (fattn.cpp: GGML_SYCL_ENABLE_MKL_FA, GGML_SYCL_MKL_FA_DEBUG, GGML_SYCL_MKL_FA_DIAG; fattn-mkl.cpp: GGML_SYCL_MKL_FA_DEBUG) - Remove duplicated stream->wait() before ev.wait_and_throw() in GEMM KQ and GEMM VKQ — ev.wait_and_throw() already waits for completion - Gate MKL_ACCUM macro behind do_print so timing accumulators are no-ops in normal operation - Remove redundant MIT/Intel copyright header from fattn-mkl.cpp - Remove unused #include <cfloat> - Expand SYCL.md MKL FA docs with step-by-step activation trigger and example llama-cli command Again, thank you!!! Co-Authored-By: Claude Code on DeepSeek-v4-Pro * fattn-mkl: enable MKL FA for all KV cache types Remove the quantized-only restriction on MKL activation — the MKL kernel converts any non-F16 K/V to F16 via to_fp16_sycl before GEMM, so F16 (default), BF16, and F32 caches all benefit from XMX hardware acceleration. The type restriction was an unnecessary gate. Before (F16/BF16 default cache + FA on at 32K prefill): ~356 t/s (TILE path) After: ~670 t/s (MKL path, matching quantized-cache baseline) Minimal change: two conditions removed, one comment updated in fattn.cpp. No kernel or conversion code changes — the dequant pipeline already covers all types. * fattn-mkl: rename mkl_disable -> mkl_enable for clarity * fattn-mkl: refine MKL FA dispatch gates Three changes: 1. Remove quantized-only restriction - MKL FA activates for all KV cache types (F16 default, BF16, F32, quantized). The MKL kernel converts non-F16 K/V via to_fp16_sycl before GEMM. 2. Rename mkl_disable -> mkl_enable to match env var (GGML_SYCL_ENABLE_MKL_FA). 3. Replace batch-size threshold with Q->ne[1] >= 32 gate. Keeps TG (Q=1) and MTP drafts (Q=3-8) on VEC path where fused kernel beats MKL launch overhead. Routes all multi-token prefill through XMX-accelerated GEMM. Production data confirms Q patterns: 1-8 TG, 32-127 cache reuse, 128+ full reprocess. At 32K F16/BF16 FA-on: 356 -> 670 t/s. * ggml-sycl: fix F16 cache + MKL FA multi-turn corruption; add gate guards Two changes: 1. Always copy F16 K/V to dense row-major buffers before MKL GEMM. Previously F16 was read in-place with raw tensor strides. During multi-turn conversations, the accumulated KV cache had different stride properties than a fresh prefill, producing corrupted outputs. Now dense F16 gets a fast memcpy; interleaved (Gemma) gets a strided copy kernel. This matches what the quantized paths already did through to_fp16_sycl. 2. Gate MKL FA on unsupported op params (max_bias, logit_softcap, batch dim mismatch) and pathological F16 strides (nb[1] not a multiple of ne[0]*2). These conditions would previously crash inside the MKL kernel. Pathological strides (test-only) and ALiBi/softcap fall through to TILE/VEC which handle them correctly. The stride check uses modulo rather than equality, so both dense (nb1 == ne0*2) and interleaved (nb1 == H * ne0*2) pass — all real models use these layouts. Only test cases with overlapping rows (nb1=32 or nb1=75 for ne0=40) are blocked. Thanks to hmscider for the oneDNN FA PR (ggml-org#25222) which surfaced the same insight: always normalize inputs to contiguous F16 before GEMM. Co-Authored-By: Claude Code using DeepSeek-V4-Pro <noreply@anthropic.com> * fattn-mkl: fix quant+GQA KV strides, tighten MKL gate, add K>=1024 tests Adding K>=1024 flash-attn test cases surfaced several MKL bugs: - Quant K/V with a padded seq-view (real KV cache) used the wrong strides in the dequant path... only the true Gemma interleave layout should reconstruct strides. nb[2] vs ne[1]*nb[1] - Gate was firing on shapes the kernel doesn't handle: head_dim < 64 or not a multiple of 64, MHA, attention sinks, and bf16 decode... fell through to vec which no bf16 case. Gate MKL to the validated envelope: gqa>=2, head_dim 64 through 512 (has to be a multiple of 64) with matching K/V head size, mask, no sinks/alibi/softcap... everything else falls back to tile. Covers Qwen Dense/MoE and Gemma4 Dense/MoE Ran test-backend-ops -o FLASH_ATTN_EXT: 3641/3641 pass. Perplexity unchanged... 6.7267 MKL vs 6.7290 stock using Qwen 27b q5_k_xl * Update ggml/src/ggml-sycl/fattn.cpp Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com> * Update ggml/src/ggml-sycl/fattn.cpp Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com> * Update ggml/src/ggml-sycl/fattn.cpp Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com> * fattn-mkl: bound attention scratch so it doesn't grow with batch or context... also dropped the bf16 comment in fattn.cpp per arthw review. * Update ggml/src/ggml-sycl/fattn-mkl.cpp Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com> * Update ggml/src/ggml-sycl/fattn-mkl.cpp Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com> * apply arthw suggestions: enum for dequant modes, macro for wg_size, env-var one-liners --------- Co-authored-by: Claude Code using DeepSeek-V4-Pro <noreply@anthropic.com> Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com>
|
In code: commit 11924d4 (tag: b10223, origin/master, master) It will get performance increase in more LLMs event with fp32 building on B60: Test Script
Environment Configurations
Per Metric
|
* test new flash_attn test * rebase and fix to disable subgrou matrices when max_kv_tile == 0 * delete log output * Add i32 support to cpy and enables the all ops test * restore the non target ci tests * comment out of TODO of build-cpu.yml * fix format
ggml-org#25025) * SYCL: add oneMKL GEMM flash attention for XMX-accelerated prompt processing * fattn-mkl: fix interleaved dst layout in normalize kernel - Fix mkl_fa_normalize_head: use interleaved dst layout ((query * n_q_heads + head) * DV) matching TILE's flash_attn_combine_results. Previously used dense head-major layout which wrote head outputs to wrong addresses, corrupting attention for all models except Qwen3.6-27B (where GQA=6 heads were sparse enough to avoid visible overlap). - Remove 7 redundant stream->wait() calls — SYCL in-order queue already serializes pure SYCL kernel dependencies. Retain only the 4 MKL GEMM ↔ SYCL handshake barriers (oneMKL GEMM uses its own internal queue that does not respect SYCL in-order). - Remove unused dst_row_stride, diagnostic clutter, and dead K/V hex dump (fa_diag block in fattn-mkl.cpp). - Add MKL_FA_DISABLE=1 env var for A/B testing. - Add FA-DISP watchdog (MKL_FA_DEBUG=1) and FA-DIAG output fingerprint (MKL_FA_DIAG=1) in fattn.cpp. Tested: Gemma-4-26B, Gemma-4-31B, Qwen3.6-27B, Qwen3.6-35B-A3B Perf (B70/Battlemage, 32K, q8_0 KV): Gemma-4-26B: 1473 t/s MKL vs 746 TILE (1.97x) Qwen3.6-27B: 609 t/s MKL vs 330 TILE (1.85x) Co-Authored-By: Claude Code on DeepSeek-v4-Pro * Thank you for the review feedback: rename env vars, use GGML_LOG_INFO, document in SYCL.md Completed the following: - Rename MKL_FA_DISABLE → GGML_SYCL_ENABLE_MKL_FA (inverted: 0 to disable) - Rename MKL_FA_DEBUG → GGML_SYCL_MKL_FA_DEBUG - Rename MKL_FA_DIAG → GGML_SYCL_MKL_FA_DIAG - Replace fprintf(stderr, ...) / fflush(stderr) with GGML_LOG_INFO() macro - Document all three env vars in docs/backend/SYCL.md under Runtime - Add comment explaining MKL FA activation trigger (flash-attn + quantized KV cache + batch-size >= 1024 + n_kv >= 1024) Resolves review feedback from arthw. Again, thank you!!! Co-Authored-By: Claude Code on DeepSeek-v4-Pro * Thank you for the review feedback round 2: use ggml_sycl_get_env, remove dup waits, gate perf macros - Replace raw getenv() with ggml_sycl_get_env() in all 4 env-var checks (fattn.cpp: GGML_SYCL_ENABLE_MKL_FA, GGML_SYCL_MKL_FA_DEBUG, GGML_SYCL_MKL_FA_DIAG; fattn-mkl.cpp: GGML_SYCL_MKL_FA_DEBUG) - Remove duplicated stream->wait() before ev.wait_and_throw() in GEMM KQ and GEMM VKQ — ev.wait_and_throw() already waits for completion - Gate MKL_ACCUM macro behind do_print so timing accumulators are no-ops in normal operation - Remove redundant MIT/Intel copyright header from fattn-mkl.cpp - Remove unused #include <cfloat> - Expand SYCL.md MKL FA docs with step-by-step activation trigger and example llama-cli command Again, thank you!!! Co-Authored-By: Claude Code on DeepSeek-v4-Pro * fattn-mkl: enable MKL FA for all KV cache types Remove the quantized-only restriction on MKL activation — the MKL kernel converts any non-F16 K/V to F16 via to_fp16_sycl before GEMM, so F16 (default), BF16, and F32 caches all benefit from XMX hardware acceleration. The type restriction was an unnecessary gate. Before (F16/BF16 default cache + FA on at 32K prefill): ~356 t/s (TILE path) After: ~670 t/s (MKL path, matching quantized-cache baseline) Minimal change: two conditions removed, one comment updated in fattn.cpp. No kernel or conversion code changes — the dequant pipeline already covers all types. * fattn-mkl: rename mkl_disable -> mkl_enable for clarity * fattn-mkl: refine MKL FA dispatch gates Three changes: 1. Remove quantized-only restriction - MKL FA activates for all KV cache types (F16 default, BF16, F32, quantized). The MKL kernel converts non-F16 K/V via to_fp16_sycl before GEMM. 2. Rename mkl_disable -> mkl_enable to match env var (GGML_SYCL_ENABLE_MKL_FA). 3. Replace batch-size threshold with Q->ne[1] >= 32 gate. Keeps TG (Q=1) and MTP drafts (Q=3-8) on VEC path where fused kernel beats MKL launch overhead. Routes all multi-token prefill through XMX-accelerated GEMM. Production data confirms Q patterns: 1-8 TG, 32-127 cache reuse, 128+ full reprocess. At 32K F16/BF16 FA-on: 356 -> 670 t/s. * ggml-sycl: fix F16 cache + MKL FA multi-turn corruption; add gate guards Two changes: 1. Always copy F16 K/V to dense row-major buffers before MKL GEMM. Previously F16 was read in-place with raw tensor strides. During multi-turn conversations, the accumulated KV cache had different stride properties than a fresh prefill, producing corrupted outputs. Now dense F16 gets a fast memcpy; interleaved (Gemma) gets a strided copy kernel. This matches what the quantized paths already did through to_fp16_sycl. 2. Gate MKL FA on unsupported op params (max_bias, logit_softcap, batch dim mismatch) and pathological F16 strides (nb[1] not a multiple of ne[0]*2). These conditions would previously crash inside the MKL kernel. Pathological strides (test-only) and ALiBi/softcap fall through to TILE/VEC which handle them correctly. The stride check uses modulo rather than equality, so both dense (nb1 == ne0*2) and interleaved (nb1 == H * ne0*2) pass — all real models use these layouts. Only test cases with overlapping rows (nb1=32 or nb1=75 for ne0=40) are blocked. Thanks to hmscider for the oneDNN FA PR (ggml-org#25222) which surfaced the same insight: always normalize inputs to contiguous F16 before GEMM. Co-Authored-By: Claude Code using DeepSeek-V4-Pro <noreply@anthropic.com> * fattn-mkl: fix quant+GQA KV strides, tighten MKL gate, add K>=1024 tests Adding K>=1024 flash-attn test cases surfaced several MKL bugs: - Quant K/V with a padded seq-view (real KV cache) used the wrong strides in the dequant path... only the true Gemma interleave layout should reconstruct strides. nb[2] vs ne[1]*nb[1] - Gate was firing on shapes the kernel doesn't handle: head_dim < 64 or not a multiple of 64, MHA, attention sinks, and bf16 decode... fell through to vec which no bf16 case. Gate MKL to the validated envelope: gqa>=2, head_dim 64 through 512 (has to be a multiple of 64) with matching K/V head size, mask, no sinks/alibi/softcap... everything else falls back to tile. Covers Qwen Dense/MoE and Gemma4 Dense/MoE Ran test-backend-ops -o FLASH_ATTN_EXT: 3641/3641 pass. Perplexity unchanged... 6.7267 MKL vs 6.7290 stock using Qwen 27b q5_k_xl * Update ggml/src/ggml-sycl/fattn.cpp Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com> * Update ggml/src/ggml-sycl/fattn.cpp Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com> * Update ggml/src/ggml-sycl/fattn.cpp Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com> * fattn-mkl: bound attention scratch so it doesn't grow with batch or context... also dropped the bf16 comment in fattn.cpp per arthw review. * Update ggml/src/ggml-sycl/fattn-mkl.cpp Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com> * Update ggml/src/ggml-sycl/fattn-mkl.cpp Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com> * apply arthw suggestions: enum for dequant modes, macro for wg_size, env-var one-liners --------- Co-authored-by: Claude Code using DeepSeek-V4-Pro <noreply@anthropic.com> Co-authored-by: Neo Zhang <zhang.jianyu@outlook.com>
…ml-org#26566) * test new flash_attn test * rebase and fix to disable subgrou matrices when max_kv_tile == 0 * delete log output * Add i32 support to cpy and enables the all ops test * restore the non target ci tests * comment out of TODO of build-cpu.yml * fix format
…ml-org#26566) * test new flash_attn test * rebase and fix to disable subgrou matrices when max_kv_tile == 0 * delete log output * Add i32 support to cpy and enables the all ops test * restore the non target ci tests * comment out of TODO of build-cpu.yml * fix format
43 upstream commits, 15 of them in our paths. Four conflicts, resolved as follows. The dangerous change in this range did NOT conflict -- see (2). 1. ggml/src/ggml-sycl/element_wise.cpp -- TOOK UPSTREAM VERBATIM. ggml-org#25946 landed upstream as 11b068d. We had been carrying it as a cherry-pick of the then-unmerged PR (ca7c42a) plus two commits of our own stacked on top: 27d821e fastdiv for the strided unary index reconstruction 0595878 fastdiv for the fused-GLU index reconstruction Upstream's landed form contains BOTH optimisations by the same mechanism (init_fastdiv_values host-side + fast_div_modulo in-kernel, on the strided unary path and on all five gated_op_fused_* kernels). All three of ours are therefore superseded and are dropped; the file is now byte-identical to origin/master. Only semantic difference we give up: ours guarded k > u32 with an exact int64 fallback, upstream asserts ggml_nelements(dst) < 2^31 instead -- stricter by 2x, and unreachable for a GLU activation (~8 GB at f32). Our ne>0?ne:1 divisor guard is also dropped; init_fastdiv_values asserts d != 0 and a ggml tensor always has ne[i] >= 1, so it was defensive, not load-bearing. 2. ggml/src/ggml-sycl/fattn.cpp -- PRECEDENCE PRESERVED, both kernels kept. Upstream ggml-org#25025 adds a oneMKL GEMM flash-attention path and gives it BEST_FATTN_KERNEL_MKL = 300 -- the value we already use for BEST_FATTN_KERNEL_MMA. git flagged the enum collision. It did NOT flag the consequential half: upstream places the MKL gate ABOVE our MMA check, and that hunk auto-merged clean. MKL's gate is default-ON (GGML_SYCL_ENABLE_MKL_FA=1) and its envelope -- gqa_ratio >= 2, head_dim % 64 in [64,512], Q->ne[1] >= 32, K->ne[1] >= 1024, no sinks / ALiBi / softcap, with a quantized K/V SKIPPING the F16 stride test -- matches our deploy prefill exactly. Taken verbatim it would have silently replaced the measured MMA kernel with an unmeasured one, staged the whole q8_0 KV cache to F16 first, and (per upstream's own note) broken SYCL graph capture replay. Resolution: MKL renumbered to 400 so both kernels stay reachable, and its gate takes an added !ggml_sycl_fattn_mma_supported(dst) conjunct. MMA wins where MMA is supported; MKL keeps its FULL envelope for everything MMA declines, which is upstream's intent in every case that is not ours. This is a precedence choice, not a revert -- and it is A/B-able without a rebuild: GGML_SYCL_FATTN_MMA=0 -> MMA declines, MKL takes the path GGML_SYCL_ENABLE_MKL_FA=0 -> MKL off entirely Also merged both sides' env-gated instruments, hoisting the kernel selection to a single call: upstream re-derived it three times (watchdog, switch, fingerprint), so an instrument could disagree with what actually ran. All three now read one hoisted `k`. Fixed a latent lie in our own FATTN_DEBUG printer while there -- BEST_FATTN_KERNEL_ONEDNN was printing as "NONE"; ONEDNN and MKL now print. 3. ggml/src/ggml-sycl/cpy.cpp -- kept ours. Upstream's side of the hunk was empty; ggml-org#26005 touched adjacent lines. Our GGML_SYCL_CPY_CENSUS instrument is unchanged. 4. tests/test-backend-ops.cpp -- kept both sides, additive and disjoint (same resolution as the 07-28 sync). Ours = the finding-85 deployed-shape MUL_MAT sweep; upstream's = m==1 either side of MMVF_MAX_BATCH_SIZE. Gates run before this commit: - cmake-option-audit.sh e9fa078 origin/master -> 7/7 watched options unchanged, and --selftest fires (rc=3), so the check is proven able to go red. - post-configure asserts: GGML_SYCL / _F16 / _DNN / _GRAPH all ON. NOT done in this commit, and required before any number from this tree is comparable to a pre-sync one: rebuild + re-baseline. Upstream changed code under every arm; ratios within one arm survive, absolutes do not.
…ml-org#26566) * test new flash_attn test * rebase and fix to disable subgrou matrices when max_kv_tile == 0 * delete log output * Add i32 support to cpy and enables the all ops test * restore the non target ci tests * comment out of TODO of build-cpu.yml * fix format
…ml-org#26566) * test new flash_attn test * rebase and fix to disable subgrou matrices when max_kv_tile == 0 * delete log output * Add i32 support to cpy and enables the all ops test * restore the non target ci tests * comment out of TODO of build-cpu.yml * fix format
…ml-org#26566) * test new flash_attn test * rebase and fix to disable subgrou matrices when max_kv_tile == 0 * delete log output * Add i32 support to cpy and enables the all ops test * restore the non target ci tests * comment out of TODO of build-cpu.yml * fix format
Adds a flash attention path that routes Q·K^T and S·V matrix multiplies
through oneMKL GEMM, enabling XMX hardware acceleration on Intel GPUs.
Motivation
The existing SYCL flash attention kernels (VEC, TILE) run entirely in
SYCL subgroup operations. On Intel Arc GPUs with XMX matrix engines
(Battlemage and later), oneMKL GEMM can process the large matmuls in
attention significantly faster — particularly at high context lengths.
where the KV cache is quantized.When it activates
- KV cache is quantized (q8_0, q4_0, q4_1, q5_0, q5_1, or any K-quant)--flash-attn onor-fa)- Q sequence length ≥ 128while keeping single-token decode and MTP spec drafts on the existing
TG-optimized VEC kernel)
~~These thresholds route prompt processing through MKL while leaving
single-token decode to the existing TG-optimized kernels. ~~
The path is never activated for f16/bf16 KV cache — those already perform well with the TILE kernel and graph capture.All KV cache types benefit from XMX acceleration — F16 (the default),
BF16, F32, and quantized (q8_0, q4_0, q4_1, q5_0, q5_1, K-quant).
The MKL kernel converts non-F16 K/V to F16 via
to_fp16_syclbeforeGEMM, so no additional conversion code was needed.
Set
GGML_SYCL_ENABLE_MKL_FA=0to force the TILE/VEC path for A/Btesting or power comparison.
Implementation
All logic is in one new file,
fattn-mkl.cpp(567 lines). The pipeline:GQA groups sharing a KV head are batched into single GEMM calls —
6 query heads × 1020 tokens = 6120 rows in one MKL call, amortizing
launch overhead.
All benchmarks: Qwen 3.6 27B UD Q5_K_XL, MTP enabled.
For comparison, stock bf16 KV cache + FA off on the same GPU achieves 822 t/s PP at 8K — the MKL path with q8_0 is within 1% while using quantized memory.The f16/bf16 numbers are with FA-on and the MKL path — matching the
quantized-cache baseline that previously required
--cache-type-k q8_0Testing
quant types, head sizes 64–512, causal/non-causal masks, sinks,
max_bias, GQA ratios, multi-batch)
no coherence errors
MKL path correctly handles the reprocess delta
BEST_FATTN_KERNEL_MKLenum; other backends and non-quantizedpaths are completely unaffected
Known limitations
incompatible with SYCL command graph replay. The existing
GGML_SYCL_DISABLE_GRAPHdefault (1) handles this.max_bias == 0.0fasserted; models needing ALiBiwill fall through to the TILE kernel.
dst->src[4]is not yet supported.Debug output
Timing instrumentation is gated behindMKL_FA_DEBUG=1. In normal operation the MKL path produces no output.Timing instrumentation is gated behind
GGML_SYCL_MKL_FA_DEBUG=1.Output fingerprint for correctness verification is available with
GGML_SYCL_MKL_FA_DIAG=1. In normal operation the MKL path producesno output.
AI disclosure
Claude Code was used for SYCL boilerplate (ND-range kernel launches,
ggml_sycl_pool_alloc patterns) and initial drafting of the chunked KV
loop. All algorithmic decisions — oneMKL GEMM integration, online
softmax with GQA batching, activation thresholds, chunk sizing — were
human-directed. Comprehensive testing (3605 test-backend-ops,
multi-quant and multi-batch coherence validation, performance
benchmarking at contexts up to 110K) was performed manually.
🤖 Generated with Claude Code using DeepSeek-V4-Pro