快速结论:在 Apple Silicon(Metal 后端)上用 llama.cpp 跑 Gemma 26B 系列模型做 token 生成时,若发现相对 b10908 及之前版本速度下降约 6–9%,通常是自 #28164(b10909 起)引入的 fusion packing 变更把多个 pattern 串成一个 pack,导致 concurrency pass 可用节点变少。优先排查是否命中了 ggml_metal_fusion_max() 的多 pattern 链接逻辑。
适用环境:macOS 26.5.1(Darwin arm64),AppleClang 21.0.0 / Xcode 26.6,CMake 4.4.3、Ninja 1.13.2;llama.cpp 版本 0.4.1-dev(tag b11050,commit 60b06ab 一类的构建),编译选项 -DGGML_METAL=ON -DGGML_METAL_EMBED_LIBRARY=ON;硬件 Apple M2 Ultra 128 GB / M3 Ultra 96 GB;模型 gemma-4-26B-A4B-it-Q4_K_M.gguf(MoE 26B/A4B)或 Ollama gemma4:26b-a4b-it-qat(Q4_0)。
最快修复方案:Issue 中已验证:让 ggml-metal-fusion.cpp 的 ggml_metal_fusion_max() 每次调用只打包一个 pattern(=一个 fused kernel),不再把相邻 pattern 串成一个 pack。报告者在 M2 Ultra 与 M3 Ultra 上均复现出恢复,单项 pattern packing 后速度回到父版本约 0.14% 以内。
注意事项:该修复为源码级改动,需要自行重新编译;Issue 中未确认是否已合入上游正式版本,也未有官方 follow-up PR 指针,因此作为补丁使用时需关注是否与当前 master 代码冲突。合入后需重新验证 prompt processing 是否受影响(Issue 中 pp 不受该 commit 影响)。
问题场景
在 Apple Silicon Mac 上使用 llama.cpp 的 Metal 后端运行 Gemma 26B 系列模型(如 gemma-4-26B-A4B-it-Q4_K_M.gguf 或 Ollama 的 gemma4:26b-a4b-it-qat)进行 token 生成。复现命令为 llama-bench -m gemma-4-26B-A4B-it-Q4_K_M.gguf -p 512 -n 128 -r 3,也可在 llama-server 中用固定 prompt 连续请求测算 timings.predicted_per_second。问题自 PR #28164(b10909,commit a2878d30)起出现,Prompt processing 不受影响。
报错原文
Metal: ~6% token-generation regression on gemma-4-26B-A4B since #28164 (fusion packing chains patterns, reducing concurrency)
原因分析
PR #28164 重写了 Metal fusion 表,其中 ggml_metal_fusion_max() 增加了 while (i_f < n_idxs && total < GGML_METAL_FUSION_MAX) 循环,把多个 ggml_can_fuse 链背靠背打包进同一个 pack。旧实现(ggml-metal-common.cpp)每次只打包一条融合链。
在这个模型上表现为:MoE 输出处的 ADD x7 被与下一个 block 的 RMS_NORM+MUL 粘进同一个 pack(f=9);同一层的两条分支 norm(ffn_mlp 与 ffn_moe)由原本两个独立 pack 合并为一个 pack(f=4)。gemma4-26B-A4B 每层同时有 dense MLP 分支和 MoE 分支,把两条分支的输入 norm 粘进同一个 pack 后,损失了 concurrency pass 原本拥有的重排自由度,因此参与 (concurrent) 编码的节点从 720 降到 662,token 生成性能随之下降。编码出的 kernel 本身没变(9 个节点的 pack 仍被编码为 ADD x7 + RMS_NORM+MUL),改变的只是 packing 粒度。使用 GGML_METAL_FUSION_DEBUG=2 可确认各 pattern 匹配计数在改动前后完全一致,说明不是 pattern 停止匹配导致。
环境排查
- 确认 llama.cpp 版本/commit:b10908 为最后正常版本,b10909(= a2878d30,PR #28164)为首个异常版本;b11050 仍存在该问题。
- 确认 macOS 版本(报告为 macOS 26.5.1)、Xcode / AppleClang 版本(AppleClang 21.0.0 系列)。
- 确认 Metal 后端是否启用(
-DGGML_METAL=ON -DGGML_METAL_EMBED_LIBRARY=ON)。 - 确认硬件为 Apple Silicon(M2 Ultra 128 GB 或 M3 Ultra 96 GB)。
- 确认模型为 Gemma 26B 系列(
gemma-4-26B-A4B-it-Q4_K_M.gguf或 QAT Q4_0),其他模型(如 Qwen3.8-Flash-Next、gemma-4-e4b)报告未受影响。 - 可用对比开关定位:
GGML_METAL_CONCURRENCY_DISABLE=1、GGML_METAL_GRAPH_OPTIMIZE_DISABLE=1、GGML_METAL_FUSION_DISABLE=1以及GGML_METAL_GRAPH_DEBUG=1查看 concurrent 节点数。
解决步骤
- 先做基线复现:在 b10908 与 b10909 之间用
llama-bench -m gemma-4-26B-A4B-it-Q4_K_M.gguf -p 512 -n 128 -r 3对比 tg128,确认存在约 6% 的差距。 - 用
GGML_METAL_GRAPH_DEBUG=1统计 decode graph 中被标记为(concurrent)的节点数,正常版本约 720,异常版本约 662。 - 打开
ggml/src/ggml-metal/ggml-metal-fusion.cpp,定位ggml_metal_fusion_max()中把相邻 pattern 累加进同一 pack 的while循环、无匹配时的检查以及最终返回逻辑。 - 改为每次只打包一个 pattern(即一个 fused kernel),不再向后链接多个 pattern:对应报告中的 diff 思路是去掉
total/i_f累加循环,仅调用一次ggml_metal_fusion_next()并据此返回长度。 - 用同一套 Xcode 工具链、CMake/Ninja 重新编译(Release、
GGML_METAL=ON、GGML_METAL_EMBED_LIBRARY=ON)。 - 在同一模型上重新跑
llama-bench或llama-server计时,确认 tg128 回到 b10908 附近水平。
验证方法
重新编译后运行与之前相同的 llama-bench -p 512 -n 128 -r 3,观察 tg128 是否恢复到 b10908 的约 85 t/s 或 M3 Ultra 上父版本的约 102 t/s 水平;同时检查 GGML_METAL_GRAPH_DEBUG=1 输出的 (concurrent) 节点数是否回到约 720。报告者在 M3 Ultra 上验证单品 pattern packing 的恢复结果与父版本仅差 0.14%,且生成文本与 token 序列保持一致(text SHA-256 未变),说明修复不改变生成语义。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。
![[Bug]: Knowledge base creators cannot access datasets created via upload or RAG Pipeline](https://www.chat-gpts.plus/wp-content/uploads/2026/09/42836-411fb8f3-768x403.jpg)
![[Bug]: Multi card issue and multi token prediction (mtp) issue with Intel/Qwen3.6-35B-A3B-int4-mixed-AutoRound](https://www.chat-gpts.plus/wp-content/uploads/2026/09/53119-9a400882-768x403.jpg)
