Vulkan: wrong matmul results on Adreno (subgroupSize=128) — warptile WM exceeds BM (implicit `WM 64)

此报错主要出现在 Vulkan 后端的 Adreno 系列 GPU(子组大小 subgroupSize=128)上,运行 batched、broadcast、partial-tile 形状的矩阵乘法时导致 matmul 结果错误。优先排查并修复 ggml-vulkan 中 warptile 的 WA

快速结论:此报错主要出现在 Vulkan 后端的 Adreno 系列 GPU(子组大小 subgroupSize=128)上,运行 batched、broadcast、partial-tile 形状的矩阵乘法时导致 matmul 结果错误。优先排查并修复 ggml-vulkan 中 warptile 的 WARP 值裁剪,将 l_/m_ warptile 的 WARP 强制限制在 64。

适用环境:llama.cpp(commit bf2c86d),Vulkan 后端,Adreno 650(Mesa Turnip 驱动),设备报告 subgroupSize=128。同样影响 whisper.cpp 内置的 ggml 树,以及任何报告 subgroupSize > 64 的设备。

最快修复方案:在 ggml_vk_load_shaders() 中新增变量 mm_warp_8 = std::min(subgroup_size_8, 64u),并将所有 l_warptile*/m_warptile* 定义中的 subgroup_size_8 替换为 mm_warp_8(共 18 行)。同时为 mmqid 的 l_/m_ warptile 新增 mul_mat_mm_warp_8 = std::min(mul_mat_subgroup_size_8, 64u) 并替换。s_warptile 必须保留未裁剪的值。

注意事项:s_warptile 的 BLOCK_SIZE 是 subgroup_size_32,需要 NUM_WARPS==1 保持面积 BM*BN,裁剪它会导致 m=1(矩阵-向量)性能回退,已实测验证。裁剪对 subgroupSize ≤ 64 的设备无影响(min 为 no-op)。

问题场景

用户在 Volcano 后端(Vulkan)使用 llama.cpp 或 whisper.cpp 时触发。具体操作包括运行 ./test-backend-ops -b Vulkan0 -o MUL_MAT 进行测试,或运行 whisper.cpp 实际工作负载。症状为 GPU 上产生乱码/循环输出,而同一模型在 CPU 后端上正确。

报错原文

MUL_MAT(type_a=f16,type_b=f32,m=64, n=45,k=128,bs=[8,1],nr=[4,1])  ERR = 0.414  FAIL
MUL_MAT(type_a=f16,type_b=f32,m=128,n=45,k=64, bs=[8,1],nr=[4,1])  ERR = 1.079  FAIL

Vulkan: wrong matmul results on Adreno (subgroupSize=128) — warptile WM exceeds BM (implicit `WM  64)

原因分析

在 ggml_vk_load_shaders() 中,warptiles 的构建使用 WM(索引 4)和 WARP(索引 10)= subgroup_size_8 = max(device->subgroup_size, 8),隐含不变式 WM <= BM。当 subgroupSize ∈ {8,16,32,64} 时成立,但在 128 时被打破:m_warptile 出现 WM=128 > BM=64,l_warptile 出现 WM=256 > BM=128。

在 mul_mm.comp 中,退化 tile 导致三种故障:

  • BM / WM 折叠为 0(无符号),warp_r/warp_c 出现 udiv/umod 除零。
  • buf_a 共享内存索引访问 0..WM-1 行,但 buf_a 只有 BM*SHMEM_STRIDE 个条目;WM>BM 时越界进入 buf_b,A 操作数变成 B 数据。
  • BN 输出列中只有前 WN 列被写入,WN..BN-1 列永不计算/存储。

小 tile(s_warptile)使用 BM = WM = subgroup_size_32,保持自洽,因此只有中/大 tile 失败(需要 n>32 且 m>32)。

环境排查

  • 确认设备报告的 subgroupSize 是否大于 64(如 Adreno 650 经 Mesa Turnip 为 128)。
  • 确认 llama.cpp 或 whisper.cpp 使用的 ggml 版本是否包含问题提交(如 bf2c86d)。
  • 确认 Vulkan 后端设备已正确枚举(可通过 test-backend-ops 验证)。
  • 若使用 whisper.cpp,其内置 ggml 树需单独打补丁,不能只修 llama.cpp 的树。

解决步骤

  1. 找到 ggml/src/ggml-vulkan/ggml-vulkan.cpp 中的 ggml_vk_load_shaders()。
  2. 新增两行:
    const uint32_t mm_warp_8 = std::min(subgroup_size_8, 64u);
    const uint32_t mul_mat_mm_warp_8 = std::min(mul_mat_subgroup_size_8, 64u);
  3. 将 l_warptile*/m_warptile* 定义中的 subgroup_size_8 替换为 mm_warp_8,mul_mat 相关 warptile 中的 mul_mat_subgroup_size_8 替换为 mul_mat_mm_warp_8(约 18 行)。
  4. ⚠️ s_warptile 必须保留未裁剪的 subgroup_size_32,不要替换。
  5. 重新编译 llama.cpp,或单独 patch whisper.cpp 内置的 ggml 树后重新编译。

验证方法

运行 ./test-backend-ops -b Vulkan0 -o MUL_MAT,确认 f16 229/229 和 f32 173/173 全部通过,不再有 FAIL。
运行 whisper.cpp 实际任务,确认输出不再出现乱码/循环,与 CPU 后端结果一致。

参考来源

ggml-org/llama.cpp #25734

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 20534

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注