Skip to content

gpu: intel: sdpa: add configs for small head sizes with a thin q - #5979

Merged
karturov merged 2 commits into
uxlfoundation:mainfrom
karturov:karturov/sdpa-xe2-f32-small-head
Sep 13, 2026
Merged

karturov merged 2 commits into
uxlfoundation:mainfrom
karturov:karturov/sdpa-xe2-f32-small-head

Conversation

@karturov

@karturov karturov commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

This PR addresses MFDNN-15542.

Summary

SDPA with a thin Q (second token) and a small head size selects a work-group tile far larger than the whole problem, on both Xe2 and Xe3p. Adding four config rows with a 16x16 tile gives 3.6x-19.5x on the affected shapes.

Root cause

criteria_matches() compares second_token exactly. The existing {xe2, 16, fma} row does not carry second_token, so a thin-q query can never select it and falls through to the head_size=32 rows, which are tuned for a 32-wide head and a longer sequence:

path selected config KQ tile problem
f32 16,16,16,16,8,2,8,2 128x32 16x16
xf16 16,64,16,16,8,1,2,4 128x64 16x16

Up to 8x wasted down the query axis and 4x across the keys, on top of padding a small head size into a 32-wide register tile.

Xe3p has the same gap, and a wider one. Its smallest thin-q rows are head_size=64 for the FMA path (16,16,16,16,8,4,8,4, a 128x64 tile) and head_size=128 for the systolic path (32,32,32,32,4,1,4,1, a 128x32 tile). choose_config() only falls back to an older arch when nothing matches, so Xe3p matches its own rows and never reaches the Xe2 table.

Xe3 needs no new rowschoose_config() maps it onto the Xe2 table (arch_query = (arch == xe3) ? xe2 : arch), so it inherits the Xe2 entries for free. Confirmed on Panther Lake H.

Two rows per arch are required because the f32 query sets the fma property, which is also compared exactly, so one row cannot serve both dtypes.

Results

B=25088 H=4 (the layout reported for tnt_s_patch16_224), times in ms.

Arc B580 (Xe2, OCL)

D S f32 before after spd bf16 before after spd f16 before after spd
6 8 4.900 0.660 7.4x 5.959 0.628 9.5x 6.190 0.627 9.9x
6 16 5.624 0.909 6.2x 6.317 0.712 8.9x 6.512 0.708 9.2x
8 8 4.361 0.579 7.5x 4.433 0.255 17.4x 4.558 0.257 17.7x
8 16 4.876 0.826 5.9x 4.491 0.297 15.1x 4.623 0.306 15.1x
16 8 4.934 0.664 7.4x 4.530 0.261 17.3x 4.660 0.277 16.7x
16 16 5.500 1.067 5.2x 4.579 0.447 10.2x 4.702 0.519 9.0x

Panther Lake H (Xe3, OCL) — no table change, inherits the Xe2 rows

D S f32 before after spd bf16 before after spd
6 8 8.588 1.171 7.3x 11.321 1.145 9.9x
6 16 9.741 1.612 6.0x 12.188 1.442 8.5x
8 8 7.821 1.034 7.6x 8.015 0.575 13.9x
8 16 8.288 1.652 5.0x 8.337 0.822 10.1x
16 8 9.998 1.620 6.2x 8.223 0.805 10.2x
16 16 11.952 3.283 3.6x 8.393 1.643 5.1x

Novalake P (Xe3p, OCL)

D S f32 before after spd bf16 before after spd
6 8 16.433 0.880 18.7x 10.558 0.687 15.4x
6 16 18.307 1.205 15.2x 11.557 0.878 13.2x
8 8 15.273 0.782 19.5x 8.677 0.646 13.4x
8 16 17.682 1.379 12.8x 9.005 0.838 10.7x
16 8 19.278 1.477 13.1x 9.011 0.786 11.5x
16 16 24.852 2.778 8.9x 9.381 1.383 6.8x

On Xe3p the 16x16 tile was also the best of eleven hand-picked candidates swept with SDPA_CONFIG, including every neighbouring tile size.

Validation

  • benchdnn --mode=C passes for all Xe2 and Xe3 entries above.
  • On Xe3p the correctness reference is impractically slow at the 25088 batch used for the perf table, so the Xe3p rows are covered instead by the small shapes added in the second commit, which select the same config and pass --mode=C on Novalake P.
  • No shadowing: the configs selected for D=128 bf16, D=128 f32 thin-q, D=64 bf16 thin-q, and D=16 f32 with a long sequence are unchanged on both Xe2 and Xe3p. The f32 row is more specific than the xf16 one and is still preferred for f32.

Test coverage

The transformer shapes for GPU all use head_size >= 64 and a sequence of at least 384, so none of them reached the head_size <= 16 configs — the rows this PR touches had no test coverage at all. The second commit adds nine shapes to tests/benchdnn/inputs/sdpa/shapes_transformer_gpu.

Selection depends only on head size, key sequence length and whether the query is thin, so these stay tiny and cost almost nothing:

suite tests time
test_sdpa_ci 92 passed 17.7s
test_sdpa_gpu 212 passed 14.4s

Eight shapes land on the new rows, covering head sizes 6/8/16, sequences of 8 and 16, thin and non-thin Q, GQA, and batch > 1. The ninth pairs a small head size with a long sequence and deliberately stays on the non-thin-q config, guarding the narrow rows against capturing long sequences.

Verified on both B580 (Xe2) and Novalake P (Xe3p): the first eight select 16,16,16,16,1,1,1,1 and the ninth does not, on both architectures. Also passes with --mask=causal_top_left and --dir=FWD_D.

These tiny shapes additionally let me close the Xe3p correctness gap noted above — the Xe3p rows now have passing --mode=C coverage on real hardware, at f16 and bf16.

Checklist

  • Bug fix / performance improvement
  • Have you formatted the code using clang-format? (whitespace matches surrounding rows)

@karturov
karturov requested a review from a team as a code owner September 8, 2026 19:32
@github-actions github-actions Bot added the platform:gpu-intel Codeowner: @oneapi-src/onednn-gpu-intel label Sep 8, 2026
@karturov
karturov force-pushed the karturov/sdpa-xe2-f32-small-head branch from bbbaf33 to 862d23b Compare September 8, 2026 20:05
@karturov karturov changed the title gpu: intel: sdpa: add xe2 f32 thin-q config for small head sizes gpu: intel: sdpa: add xe2 configs for small head sizes with a thin q Sep 8, 2026
@karturov

karturov commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

make test
disable test_device_cpu
disable benchdnn_all
enable benchdnn_sdpa

For SDPA with a thin Q (second token) there is no Xe2 config with
head_size <= 16. The head_size=16 row carries no second_token property and
criteria_matches() compares that property exactly, so a thin-q query cannot
select it and falls through to the head_size=32 rows.

Those rows are tuned for a 32-wide head and a longer sequence, so a problem
with a short sequence gets a work-group tile far larger than the whole
problem: { 16, 16, 16, 16, 8, 2, 8, 2 } is a 128x32 KQ tile for f32, and
{ 16, 64, 16, 16, 8, 1, 2, 4 } is a 128x64 KQ tile for xf16, both applied to
a 16x16 problem. That wastes up to 8x down the query axis and 4x across the
keys, on top of padding a small head size into a 32-wide register tile.

Xe3p has the same gap, and a wider one. Its smallest thin-q rows are
head_size=64 for the f32 FMA path, { 16, 16, 16, 16, 8, 4, 8, 4 } (a 128x64 KQ
tile), and head_size=128 for the systolic path, { 32, 32, 32, 32, 4, 1, 4, 1 }
(a 128x32 KQ tile). choose_config() only falls back to an older arch when
nothing matches at all, so Xe3p matches these rows and never reaches the Xe2
table.

Add rows whose tile is 16x16, for both Xe2 and Xe3p, and in each case one for
the f32 FMA path and one for the xf16 systolic path. A single row cannot serve
both dtypes, because the f32 query sets the fma property and criteria_matches()
also compares that exactly. Xe3 needs no new rows: choose_config() maps it onto
the Xe2 table, so it picks up the Xe2 entries.

Measured with B=25088 H=4, the tensor layout reported for tnt_s_patch16_224.
Times in ms, before/after the change.

Arc B580 (Xe2, OCL):

          f32                    bf16                   f16
   D   S  before   after  spd    before   after  spd    before   after  spd
   6   8  4.900    0.660  7.4x   5.959    0.628  9.5x   6.190    0.627  9.9x
   6  16  5.624    0.909  6.2x   6.317    0.712  8.9x   6.512    0.708  9.2x
   8   8  4.361    0.579  7.5x   4.433    0.255 17.4x   4.558    0.257 17.7x
   8  16  4.876    0.826  5.9x   4.491    0.297 15.1x   4.623    0.306 15.1x
  16   8  4.934    0.664  7.4x   4.530    0.261 17.3x   4.660    0.277 16.7x
  16  16  5.500    1.067  5.2x   4.579    0.447 10.2x   4.702    0.519  9.0x

Panther Lake H (Xe3, OCL):

          f32                    bf16
   D   S  before   after  spd    before   after  spd
   6   8   8.588   1.171  7.3x   11.321   1.145  9.9x
   6  16   9.741   1.612  6.0x   12.188   1.442  8.5x
   8   8   7.821   1.034  7.6x    8.015   0.575 13.9x
   8  16   8.288   1.652  5.0x    8.337   0.822 10.1x
  16   8   9.998   1.620  6.2x    8.223   0.805 10.2x
  16  16  11.952   3.283  3.6x    8.393   1.643  5.1x

Novalake P (Xe3p, OCL):

          f32                    bf16
   D   S  before   after  spd    before   after  spd
   6   8  16.433   0.880 18.7x   10.558   0.687 15.4x
   6  16  18.307   1.205 15.2x   11.557   0.878 13.2x
   8   8  15.273   0.782 19.5x    8.677   0.646 13.4x
   8  16  17.682   1.379 12.8x    9.005   0.838 10.7x
  16   8  19.278   1.477 13.1x    9.011   0.786 11.5x
  16  16  24.852   2.778  8.9x    9.381   1.383  6.8x

benchdnn --mode=C passes for all of the Xe2 and Xe3 entries above. The Xe3p
system available for this work could not run the correctness reference for a
batch this large in reasonable time, so the Xe3p rows are verified by selection
and performance only; they use the same tile as the Xe2 rows, which do pass.

The new criteria are narrow enough that the configs selected for D=128 bf16,
D=128 f32 with a thin q, D=64 bf16 with a thin q, and D=16 f32 with a long
sequence are unchanged on both Xe2 and Xe3p. The f32 row is more specific than
the xf16 one and so is still preferred for f32.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@karturov
karturov force-pushed the karturov/sdpa-xe2-f32-small-head branch from 862d23b to 9cc0c4e Compare September 9, 2026 23:08
@karturov karturov changed the title gpu: intel: sdpa: add xe2 configs for small head sizes with a thin q gpu: intel: sdpa: add configs for small head sizes with a thin q Sep 9, 2026
The transformer shapes for GPU all use head_size >= 64 and a sequence of at
least 384, so none of them reach the head_size <= 16 configs. Add nine shapes
that do.

Selection only depends on head_size, the key sequence length and whether the
query is thin, so these can be kept tiny and cost little: the whole file still
runs in under twenty seconds. head_size=6 mirrors tnt_s_patch16_224.

Eight of the shapes land on the head_size=16 rows, covering head sizes 6, 8 and
16, sequences of 8 and 16, a thin and a non-thin query, GQA, and batch > 1. The
ninth pairs a small head size with a long sequence and stays on the non-thin-q
config, guarding the narrow rows against capturing long sequences.

Verified on Arc B580 (Xe2) and Novalake P (Xe3p): the first eight select
{ 16, 16, 16, 16, 1, 1, 1, 1 } and the ninth does not, on both. The full
test_sdpa_gpu and test_sdpa_ci batches pass on B580, for f16 and bf16, with no
mask, with a causal mask and with --dir=FWD_D.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@karturov
karturov requested a review from a team as a code owner September 9, 2026 23:49
@github-actions github-actions Bot added the component:tests Codeowner: @oneapi-src/onednn-arch label Sep 9, 2026
@karturov

Copy link
Copy Markdown
Contributor Author

make test perf-gpu
set primitive=sdpa

@karturov

Copy link
Copy Markdown
Contributor Author

Perf CI checked:

  • PVC not reproduced manually (noise?)
  • Another run passed on NVLP. It should be noted that some tests are skipped: MFDNN-15566 filed.

@karturov karturov closed this Sep 13, 2026
@karturov karturov reopened this Sep 13, 2026
@karturov
karturov merged commit 929ec8c into uxlfoundation:main Sep 13, 2026
46 of 52 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component:tests Codeowner: @oneapi-src/onednn-arch platform:gpu-intel Codeowner: @oneapi-src/onednn-gpu-intel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants