馃悰 Describe the bug
Any model lowered to Vulkan with weight-only int8 linear aborts on its first execute. The AOT side produces the op, the partitioner accepts it, and the runtime then asks for a shader that is not in the repo.
The chain:
FuseQuantizedOpsTransform rewrites a dequantize_per_channel + linear pair to aten._weight_int8pack_mm (backends/vulkan/_passes/fuse_quantized_ops.py, fuse_into_linear_qcnw_node).
op_registry.py lists exir_ops.edge.aten._weight_int8pack_mm.default as supported, so VulkanPartitioner takes it.
QuantizedLinearQCSNW.cpp registers it and builds the kernel name linear_qcs8w_tiled / linear_qcs8w_coop.
- No
linear_qcs8w* glsl or yaml exists. c1ea7e9221 [ET-VK] Quantized Int8 Linear (#14041) introduced linear_q8csw_tiled (letters transposed) with a different suffix scheme, and the C++ was never moved over.
On device:
libc++abi: terminating due to uncaught exception of type vkcompute::vkapi::Error:
Exception raised from get_shader_info at .../api/ShaderRegistry.cpp:54:
(it != listings_.end()) is false! Could not find ShaderInfo with name
linear_qcs8w_tiled_texture3d_texture3d_texture2d_texture2d_half_o4x3
The maintained implementation is already there: QuantizedLinear.cpp registers et_vk.linear_q8csw on quantized_linear_impl, which uses the linear_q8csw_tiled shaders. It just has no AOT counterpart, so nothing can ever emit it, while the op that AOT does emit routes to the dead path.
et_vk.linear_qcs4w has the same problem (linear_qcs4w_tiled / _coop are also missing). PR the linked PR covers only the 8-bit case, since the group-wise 4-bit kernel is not a drop-in replacement for per-channel 4-bit.
Reproduction
Quantize any linear-heavy model weight-only and lower it to Vulkan:
from executorch.backends.vulkan.quantizer.vulkan_quantizer import (
VulkanQuantizer, get_symmetric_quantization_config)
from torchao.quantization.pt2e.quantize_pt2e import prepare_pt2e, convert_pt2e
quantizer = VulkanQuantizer().set_global(
get_symmetric_quantization_config(is_dynamic=False, weight_bits=8))
prepared = prepare_pt2e(torch.export.export(module, inputs).module(), quantizer)
prepared(*inputs)
converted = convert_pt2e(prepared)
# ... to_edge_transform_and_lower with VulkanPartitioner, then run on device
The produced .pte contains aten._weight_int8pack_mm.default (strings model.pte | grep int8pack) and aborts as above on any Vulkan device.
Measured with sentence-transformers/all-mpnet-base-v2 and multi-qa-mpnet-base-dot-v1 on a Galaxy S26 Ultra (Adreno 840), ExecuTorch 1.4.1 for the export and a runner built from the same tree. Both abort. With the fix in the linked PR they run, and match the fp32 eager reference at cosine 0.998280 / 0.995343 while the file drops from 217.9 MB to 133.2 MB and per-execution time from 79 ms to 59 ms at 382 tokens.
Note that reaching this point at all needs #22372, or _convert_scalars_to_attrs lifts the position-id add as float32 and embedding fails first.
Versions
- Export:
executorch==1.4.1, torch==2.14.0.dev20260702, torchao==0.18.0, Python 3.10, macOS arm64
- Runtime: Galaxy S26 Ultra (SM-S948B), Adreno 840, Android 16
- Also read on
main at 834a4fb020: op_registry.py:434 still lists the op, QuantizedLinearQCSNW.cpp:296 still names linear_qcs8w_tiled, and no linear_qcs8w* shader exists.
cc @SS-JIA @manuelcandales @digantdesai @cbilgin
馃悰 Describe the bug
Any model lowered to Vulkan with weight-only int8 linear aborts on its first
execute. The AOT side produces the op, the partitioner accepts it, and the runtime then asks for a shader that is not in the repo.The chain:
FuseQuantizedOpsTransformrewrites adequantize_per_channel+linearpair toaten._weight_int8pack_mm(backends/vulkan/_passes/fuse_quantized_ops.py,fuse_into_linear_qcnw_node).op_registry.pylistsexir_ops.edge.aten._weight_int8pack_mm.defaultas supported, soVulkanPartitionertakes it.QuantizedLinearQCSNW.cppregisters it and builds the kernel namelinear_qcs8w_tiled/linear_qcs8w_coop.linear_qcs8w*glsl or yaml exists.c1ea7e9221 [ET-VK] Quantized Int8 Linear (#14041)introducedlinear_q8csw_tiled(letters transposed) with a different suffix scheme, and the C++ was never moved over.On device:
The maintained implementation is already there:
QuantizedLinear.cppregisterset_vk.linear_q8cswonquantized_linear_impl, which uses thelinear_q8csw_tiledshaders. It just has no AOT counterpart, so nothing can ever emit it, while the op that AOT does emit routes to the dead path.et_vk.linear_qcs4whas the same problem (linear_qcs4w_tiled/_coopare also missing). PR the linked PR covers only the 8-bit case, since the group-wise 4-bit kernel is not a drop-in replacement for per-channel 4-bit.Reproduction
Quantize any linear-heavy model weight-only and lower it to Vulkan:
The produced
.ptecontainsaten._weight_int8pack_mm.default(strings model.pte | grep int8pack) and aborts as above on any Vulkan device.Measured with
sentence-transformers/all-mpnet-base-v2andmulti-qa-mpnet-base-dot-v1on a Galaxy S26 Ultra (Adreno 840), ExecuTorch 1.4.1 for the export and a runner built from the same tree. Both abort. With the fix in the linked PR they run, and match the fp32 eager reference at cosine 0.998280 / 0.995343 while the file drops from 217.9 MB to 133.2 MB and per-execution time from 79 ms to 59 ms at 382 tokens.Note that reaching this point at all needs #22372, or
_convert_scalars_to_attrslifts the position-id add as float32 andembeddingfails first.Versions
executorch==1.4.1,torch==2.14.0.dev20260702,torchao==0.18.0, Python 3.10, macOS arm64mainat834a4fb020:op_registry.py:434still lists the op,QuantizedLinearQCSNW.cpp:296still nameslinear_qcs8w_tiled, and nolinear_qcs8w*shader exists.cc @SS-JIA @manuelcandales @digantdesai @cbilgin