[ET-VK] Avoid the near-square local work group on Adreno - #22328
Open
msluszniak wants to merge 1 commit into
Open
[ET-VK] Avoid the near-square local work group on Adreno#22328msluszniak wants to merge 1 commit into
msluszniak wants to merge 1 commit into
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/22328
Note: Links to docs will display an error until the docs builds have been completed.
|
This PR needs a
|
Adreno drivers miscompute the tiled GEMM family of shaders for 8x8x1 and 4x16x1 local work groups: entire work groups intermittently write garbage while every other block is bit-exact. These shaders have no shared memory and no barriers and their invocations are independent, so the result cannot legitimately depend on the group shape. Fix it in pick_xy_square_lwg, which all thirteen affected dispatches share: linear, matmul, conv2d_gemm, the pointwise convs, SDPA and the quantized variants. On Adreno use a 2:1 x:y shape, which yields 16x4x1 at the default 64 threads and is the cheapest correct shape measured; other vendors keep the square shape. Fixes pytorch#22327
msluszniak
force-pushed
the
ms/vulkan-linear-lwg-adreno
branch
from
August 30, 2026 13:12
4ad33b9 to
b31b709
Compare
msluszniak
added a commit
to software-mansion-labs/executorch
that referenced
this pull request
Aug 30, 2026
Adreno drivers miscompute the tiled GEMM family for 8x8x1 and 4x16x1 local work groups: entire work groups intermittently write garbage while every other block is bit-exact. pick_hw_square_wg_size is shared by Linear, Matmul, Conv2dGemm and the pointwise convs, so all of them are affected. Use 16x4x1 on Adreno, which is correct across every run measured and is the cheapest correct shape. Upstream: pytorch/executorch#22327, pytorch/executorch#22328
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #22327.
The tiled GEMM family of shaders produces wrong results on Adreno for some local work group shapes. These shaders have no shared memory, no barriers, and fully independent invocations, so their output cannot legitimately depend on the group shape, but it does.
At M = 1500, K = N = 384 (a Whisper-tiny encoder projection) with the default
pick_xy_square_lwgshape of 8x8x1, roughly 10 to 20 percent of executions come back with a handful of wholly corrupted output blocks. The corruption granularity is exactly one work group:tile_m = 4anddiv_up_4(N)means each invocation writes a 4x4 output patch, so an 8x8 group covers 32x32. One failing run had five row runs of exactly 32 (work group y 5, 7, 9, 11, 13, every other one) crossed with columns 128 to 255 (work group x 4 to 7). Everything outside those 20 work groups was correct to 5e-07.Sweeping only the group shape, holding the same 64 invocations and the same dispatch, 60 executions each on a Snapdragon SM8850 (Adreno 840):
Only the two near-square shapes fail.
2x32x1passing while4x16x1fails rules out a simple "wide is good" reading.This is not specific to linear.
pick_xy_square_lwgis shared by thirteen dispatches acrossLinear,Matmul,Conv2dGemm,Conv1dPW,Conv2dPW,SDPA,Q8taLinear,Q8taConv2dPW,QuantizedLinearandQuantizedConvolution, andconv2d_gemmreproduces it independently (see below). Fixing the shared helper covers all of them.On Adreno it now picks a 2:1 x:y
LwgShape, which the existing D'Hondt allocation turns into 16x4x1 at the default 64 threads and which scales withrecommended_lwg_nthreads(). Other vendors keep the square shape.Cost
Whisper-tiny encoder, same device, median of 60 timed iterations, sweeping only the linear dispatch:
16x4x1 is the cheapest correct shape by a wide margin. Applied to all thirteen dispatches the whole-encoder cost is +2.9% to +5.3% depending on the variant, which buys a model that is actually correct.
Verification
Distinct outputs over 60 executions with
--dump_every_execution, Adreno 840:Cosine against XNNPACK on the same input is 0.99999726 after the change; before it fell as low as 0.72.
The last two rows matter for scoping: an earlier revision of this PR patched only
Linear.cpp, and the whisper encoder looked clean at 40 executions. It was not. Once the conv frontend moved onto the im2col + GEMM path,conv2d_gemmreproduced the same defect on its own, which is what showed the fix belonged in the shared helper rather than in one caller.I only have Adreno hardware for this, so the change is gated on
device_is_adreno(). If you would rather have one shape everywhere I am happy to drop the gate, but I cannot measure the perf effect on Mali or desktop myself.