Misc. bug: chat-template rendering is O(N²) in emitted parts (gather_string_parts)

该报错是 llama.cpp 内部 `common/jinja/` 模块中 `gather_string_parts` 函数因使用 `vector::erase` 循环合并字符串导致 O(N²) 性能退化,常见于使用 llama-server 渲染包含大量工具(tools)定义的 chat temp

快速结论:该报错是 llama.cpp 内部 `common/jinja/` 模块中 `gather_string_parts` 函数因使用 `vector::erase` 循环合并字符串导致 O(N²) 性能退化,常见于使用 llama-server 渲染包含大量工具(tools)定义的 chat template 时。优先排查方向是更新 llama.cpp 到包含修复补丁的版本。

适用环境:llama.cpp version 10252 (fe2adf0),Linux x86_64,Clang 21.1.8,影响模块为 llama-server(以及所有渲染 chat template 的工具)。

最快修复方案:暂无确认的一步修复方案。该问题已通过 PR 提交修复,建议升级到包含该修复的 llama.cpp 最新版本。

注意事项:该问题仅在工具(tools)数量较多时性能影响明显(如 20-40 个工具时渲染耗时 0.5-2 秒);补丁需验证与当前版本的兼容性,升级前建议备份现有配置。

问题场景

用户在 Linux 环境使用 llama.cpp 构建的 llama-server,通过 request 提交包含大量工具(tools)定义的 chat template 渲染请求时,出现明显的 CPU 性能退化。以工具数量 20-40 的智能体(agentic)场景为例,渲染耗时可达 0.5-2 秒/请求,且随着工具数量增加呈平方级增长。

报错原文

Misc. bug: chat-template rendering is O(N²) in emitted parts (gather_string_parts)
54.31%  < caller: jinja::runtime::gather_string_parts        (15,460 calls)
36.39%    std::vector<jinja::string_part>::_M_erase          (35,121,435 calls)
20.84%    __memcpy_avx_unaligned_erms                        (the shifting)

原因分析

可能原因:`jinja::runtime::gather_string_parts` 函数(位于 `common/jinja/runtime.h`)在合并字符串部分时,在一个循环中调用 `vector::erase`。每次 erase 都会移动后续所有元素,导致 O(N) 的合并操作实际消耗 O(N²) 时间。当渲染包含 N 个字符串片段的模板时,几乎所有片段都携带相同的 `is_input` 标志,导致每次迭代都触发 erase,整体性能退化显著。此外,该函数在一次渲染中被调用大量次数(15,460 次),每次调用都会重新运行合并操作。

环境排查

  • 确认 llama.cpp 版本是否为 10252 或更早版本(修复前的版本)
  • 检查编译工具链:Clang 21.1.8 或类似版本
  • 确认操作系统为 Linux x86_64
  • 检查使用的 chat template 类型(如 CohereForAI-c4ai-command-r-plus-tool_use.jinja)
  • 运行时可通过 perf record 或 callgrind 验证性能瓶颈

解决步骤

  1. 升级 llama.cpp 到包含修复补丁的最新版本(该问题已提交 PR 修复,建议拉取最新代码)
  2. 如无法立即升级,可尝试减少单次请求中的工具(tools)数量,降低集合规模
  3. 可优先尝试使用 patch 中提供的 O(N) 单次原地压缩算法替换 `gather_string_parts` 函数中的循环合并逻辑(需自行编译)
  4. 如果自行应用补丁,注意补丁中 `++w != r` 守卫条件不可省略,否则在未发生合并时的移动操作会退化为自移动赋值,导致字符串状态未定义

验证方法

在修复后重新编译 llama.cpp 并运行相同的 chat template 渲染请求,观察不同工具数量下的渲染耗时。对比修复前后:40 个工具 × 30 个属性时,渲染时间应从约 2.425 秒降至约 0.649 秒;100 个工具 × 100 个属性时,应从约 128.5 秒降至约 6.9 秒。同时可通过比较渲染输出的 prompt 字节大小(如 26,521 / 149,971 / 587,721 字节)确认为 byte-identical。

参考来源

ggml-org/llama.cpp #26974

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 18544

发表回复

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