Misc. bug: Performance drop of Qwen3.8 in llama.cpp with a large number of tool triggers

该性能下降问题发生在 llama.cpp 的 llama-server 使用带大量工具触发的模型(如 Qwen3.8-27B)进行长文本生成时,原因是“lazy grammar”实现了 O(n²) 复杂度的触发器搜索。优先检查你的工具调用模板是否会为每个工具生成独立的 WORD 触发器,以及是否运行

快速结论:该性能下降问题发生在 llama.cpp 的 llama-server 使用带大量工具触发的模型(如 Qwen3.8-27B)进行长文本生成时,原因是“lazy grammar”实现了 O(n²) 复杂度的触发器搜索。优先检查你的工具调用模板是否会为每个工具生成独立的 WORD 触发器,以及是否运行在包含相关修复的较新版本上。

适用环境:Linux x86_64;llama.cpp 0.1.2-dev(build 10488,commit 9d77fa172);llama-server;Qwen3.8-27B-UD-Q4_K_M.gguf 模型;GNU 15.2.0 编译器。Issue 中未提供 Python、CUDA 或显卡型号信息。

最快修复方案:暂无确认的一步修复方案。Issue 中提出的修复(将 WORD 触发器存储为字面字符串,而非正则表达式)是针对 llama.cpp 源码的改动,并未作为已发布的补丁提供。可优先尝试更新到包含相关修复的较新 llama.cpp 版本,或调整工具触发器的数量/格式。

注意事项:上述源码修复方案仅针对 WORD 触发器;如果工具格式使用真正的 PATTERN(正则)触发器,问题依然存在,因为正则触发器仍走原有的 std::regex_search() 路径。

问题场景

用户在使用 llama.cpp 的 llama-server 通过 Claude Code 调用 Qwen3.8-27B 模型时触发问题。Claude Code 在请求中传递了 139 个工具,导致长响应的生成速度从约 54 tokens/s 逐渐降至约 13–20 tokens/s。用户确认 GPU 内存、系统 RAM、GPU 时钟和 CUDA OOM 均无异常,模型采样速度正常。

报错原文

Misc. bug: Performance drop of Qwen3.8 in llama.cpp with a large number of tool triggers
5.44.955.243 I slot print_timing: id  0 | task 11605 | n_gen =   1818, tg =  54.88 t/s, tg_3s =  55.00 t/s
5.47.971.616 I slot print_timing: id  0 | task 11605 | n_gen =   1983, tg =  54.86 t/s, tg_3s =  54.70 t/s
5.50.974.076 I slot print_timing: id  0 | task 11605 | n_gen =   2147, tg =  54.84 t/s, tg_3s =  54.62 t/s
5.53.976.903 I slot print_timing: id  0 | task 11605 | n_gen =   2312, tg =  54.85 t/s, tg_3s =  54.95 t/s
...

原因分析

性能下降的根本原因在 llama.cpp 的“lazy grammar”实现中。WORD 触发器被转换为正则表达式模式,每次生成新 token 后,其文本都会被追加到 trigger_buffer,然后所有触发器模式会针对整个累积的 buffer 重新评估。在本例中,有 139 个 WORD 触发器且实际正则触发器为零。随着输出增长,trigger_buffer 变大,导致每次 token 的搜索时间近乎线性增加,整个长响应的总处理成本约为 O(n²)。

用户实测数据:buffer 487 字节约 1.09 ms/token;buffer 1,052 字节约 2.3 ms/token;buffer 20,364 字节约 44.65 ms/token;buffer 25,696 字节约 56.3 ms/token。此时 tg_3s 降至约 13 tokens/s。模型自身的采样仅约 0.34 ms/token,额外延迟主要来自语法触发器搜索。

评论补充说明:即使 Qwen3-Coder 相关问题已修复(#27679),只要格式仍会生成真正的 PATTERN 正则触发器,O(n²) 缺陷仍会存在。例如 GPT-OSS 格式在 default tool_choice: “auto” 且包含工具时会发射四个 PATTERN 触发器,实测约 27.75 ns/字符/token。

环境排查

  • 确认 llama.cpp 版本是否包含 #27679 及相关修复。
  • 确认模型文件路径及 GGUF 格式(Qwen3.8-27B-UD-Q4_K_M.gguf)。
  • 确认自定义 chat template 文件(chat_template.jinja.1)中工具触发器的声明格式。
  • 统计工具调用中 WORD 触发器和 PATTERN/regex 触发器的数量。
  • 确认 –cache-type-k/v 设置为 f16、–flash-attn on 等加载参数是否会触发其他语法路径。

解决步骤

  1. 优先尝试更新至包含 #27679 修复的 llama.cpp 新版本,并复测性能。
  2. 如果更新后问题仍存在,检查你的 chat template 是否生成真正的 PATTERN 触发器——这些触发器不在 #27679 的修复范围内。
  3. 统计触发器数量。如果 WORD 触发器数量较多,可尝试精简模板中的工具说明,减少触发器条目。
  4. 如果确认问题来自 regex 触发器,需修改源码中对 PATTERN 触发器的处理逻辑(Issue 中修改仅针对 WORD 触发器,PATTERN 路径不变)。
  5. 在源码中,WORD 触发器应改为存储为字面字符串,并用限制缓冲区(失败后仅保留 max_literal_trigger_length – 1 的后缀);PATTERN 触发器保持 std::regex_search() 路径。
  6. 重新编译后,使用相同 Claude Code 工作负载复测生成速度。

验证方法

复测相同 Claude Code 工作负载,观察 trigger_buffer 大小和生成速度。修复后,139 个 WORD 触发器、0 个 regex 触发器的场景中,trigger_buffer 应稳定在约 74 字节,触发器搜索约 4–5 µs/token,grammar_accept 约 8–9 µs/token。长生成 7,881 tokens 时平均速度应维持在约 50.20 tokens/s,推理结束后的额外生成片段不应再出现速度下降。

参考来源

ggml-org/llama.cpp #27615

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 20641

发表回复

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