[Vulkan] Reduce 4w decode overhead with specialization - #22973
mergennachin wants to merge 1 commit into
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/22973
Note: Links to docs will display an error until the docs builds have been completed. ❗ 1 Active SEVsThere are 1 currently active SEVs. If your PR is affected, please view them below: ❌ 1 New FailureAs of commit 23d7924 with merge base 3b065a6 ( NEW FAILURE - The following job has failed:
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
Eliminate the scale-cache check when each worker advances to a different quantization group, and specialize logical activation K and weight N for FP16 decode. Keep forced-kernel specialization consistent with production. Cover decode/prefill transitions and padded weights, including poisoned spare input capacity.
5967d47 to
23d7924
Compare
|
Looks like it also showed improvement on Adreno too. See #22968 (comment) |
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
Add explicit <random> and <cmath> includes to keep the test translation unit self-contained.
Review effort: Lite
Findings: None
What changed in this PR
This PR optimizes Vulkan 4-bit decode by specializing FP16 dimensions and removing unnecessary scale-cache checks, with expanded correctness coverage.
Changes:
- Specializes FP16 logical K/N and forced selector constants.
- Simplifies scale loading when scale reuse is impossible.
- Adds decode, prefill, padding, storage, dtype, and selector tests.
| File | Summary |
|---|---|
backends/vulkan/test/vulkan_compute_api_test.cpp |
Adds comprehensive decode, prefill, dispatch, and padded-weight tests. |
backends/vulkan/test/custom_ops/impl/TestFpaQ4gswLinear.cpp |
Updates forced shader specialization values. |
backends/vulkan/runtime/graph/ops/impl/Q4gswLinear.cpp |
Supplies dtype-specific logical dimension specializations. |
backends/vulkan/runtime/graph/ops/glsl/q4gsw_linear_gemv_coop__w_4x8.glsl |
Specializes dimensions and optimizes scale-cache logic. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@mergennachin has imported this pull request. If you are a Meta employee, you can view this in D121197981. |
Summary
Related to #22968.
The 4w decode kernel keeps a scale-cache check even when each lane advances by at least one quantization group and cannot reuse the previous scales. Use the existing group-size specialization constant to eliminate that check and its loop-carried state in those cases. The reuse path is retained when a lane can share scales across iterations.
For FP16 decode, specialize logical activation K and weight N to give the pipeline compiler constant loop bounds and indexing strides. K comes from the input because export can pad packed weights beyond the activation width. Forced cooperative-kernel selectors use the same specialization constants as production. FP32 keeps runtime dimensions because its controls showed a small slowdown with shape specialization. Weight packing, workgroup selection, and prefill shaders are unchanged.
On an M1 Pro / MoltenVK 1.4.1, the combined change gave the following Qwen3-0.6B 4w results:
Six alternating baseline/candidate pairs, FP16 activations, group size 32, a 41-token prompt, 214 timed decode steps per run, temperature zero, eight CPU threads, and warmup enabled. Both versions used the same exported PTE and runner dependencies, with only the Vulkan backend archive replaced. Five of six paired decode comparisons improved. All 12 runs generated identical text. These are Mac measurements; Adreno performance still needs testing.
The prefill difference is within observed run-to-run variation. Performance runs had tracing and timestamp queries disabled. Timings were collected before the padded-K fix; the compiled shaders and specialization values are unchanged for these unpadded model shapes, and timings were not rerun.
Specializing N can add pipeline-cache entries for layers with the same K and different N. Startup compilation cost was not isolated.
Test plan
The Release Vulkan backend build and all four native decode tests passed. The decode/prefill tests cover 480 graph executions across FP16/FP32, buffer/Texture3D, group sizes 32/128, optional bias, aligned and partial output tiles, and M transitions 1 → 8 → 1 → 3 → 1. They compare against an independent CPU reference and check that GEMV/GEMM dispatch follows M.
Four additional cases use logical K=12, weights padded to K=16, and NaNs in unused activation capacity. These pass for both dtypes and storage types, bringing the total to 484 graph executions. The FP16 regression test fails if decode uses padded weight K as its loop bound. A separate local probe also passed all 16 combinations of forced selectors 13–16, FP16/FP32, and buffer/Texture3D.
cmake --build build --target vulkan_backend --parallel 6 VK_DRIVER_FILES=/usr/local/share/vulkan/icd.d/MoltenVK_icd.json \ test-build/vulkan-test/vulkan_compute_api_test \ '--gtest_filter=VulkanComputeGraphTest.q4gsw_decode_*'Targeted lintrunner checks and
git diff --checkpassed. Comparing all 1,151 production SPIR-V binaries against the original baseline, exactly the 12 4w decode variants change; the other 1,139, including all prefill shaders, are byte-identical.Authored with AI assistance from OpenAI Codex.
cc @SS-JIA @manuelcandales @digantdesai @cbilgin