资讯动态

ik_llama.cpp 图缓存(Graph Reuse)机制解析:告别每个 Token 重建 GGML 计算图

发布时间:2026/9/20 22:19:07 来源:尧图企业网站定制
ik_llama.cpp 图缓存Graph Reuse机制解析告别每个 Token 重建 GGML 计算图【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp导读本篇文章聚焦 ik_llama.cpp 中一项源自社区 PR 的推理加速方案——GGML 计算图缓存graph caching / graph reuse在自回归解码的逐 Token 循环中复用已构建好的计算图结构仅就地更新随 Token 变化的 KV Cache 参数从而省去每个 Token 都重建一次计算图的开销。读完本文你将理解该方案的设计动机、性能收益边界、底层源码实现can_reuse_graph与update_cache_copies的协作以及如何在当前仓库中用命令行参数控制这一行为。背景被继承的每 Token 重建计算图问题在 llama.cpp 系推理栈中每次解码一个 Token 都要经过构建计算图build graph→ 分配后端缓冲区sched alloc→ 调度执行sched compute三步。图构建发生在 CPU 侧而图计算主要发生在 GPU 侧。对于小模型GPU 计算一个 Token 的时间很短此时 CPU 侧反复构建计算图的时间占比就会显著放大成为解码吞吐t/s的隐形瓶颈。这正是 2024 年 10 月 ik_llama.cpp 仓库中 PR #94 要解决的问题。该 PR 由项目作者 ikawrakow 发起目标是把社区开发者 agray3 提交给 mainline llama.cpp 的 [PR-8366] 方案引入本项目。作者在 PR 描述中直言这种为每个新 Token 重建计算图的特性是从 llama.cpp 继承来的、自己并不喜欢的功能而 agray3 的方案让他可以避免为每个新 Token 重建计算图。注PR #94 与后续 PR #98 - Avoid rebuild of GGML graph for each token 内容完全一致。为了让贡献正确归属于 agray3PR #94 最终于 2024-10-20 关闭合并工作转移到 agray3 本人提交的 PR #98 中进行。方案核心缓存计算图就地更新 KV Cache 参数根据 PR #98 的描述方案的核心思路非常清晰引入 GGML 图缓存解码循环中不再每次都完整重建计算图而是复用上一次构建好的图结构KV Cache 参数直接更新到缓存图中每次 Token 解码后 KV Cache 的位置写入位置、已用长度等都会变化这些变化不再触发图重建而是直接修改缓存图中对应的参数可开关通过GGML_DISABLE_GRAPH_CACHING环境变量可以禁用该行为。这一思路之所以可行是因为在标准自回归解码中计算图的拓扑结构节点类型、连接关系、张量形状在连续 Token 之间通常是稳定的真正变化的只有 KV Cache 的写入偏移等运行时状态。性能收益官方基准数据PR #94 的描述中给出了方案在两种硬件平台上的实测数据。需要说明的是这些数据是 2024 年 10 月 PR 当时环境下CUDA 侧为 RTX-4080 Ryzen-7950X CPUMetal 侧为 M2-Max 30 核 GPU以tg128128 Token 解码测试项测得的仅用于展示该方案在当时环境下的收益量级。CUDARTX-4080 Ryzen-7950Xmodelsizeparamstestt/s (main)t/s (PR)Speedupllama 8B Q4_04.33 GiB8.03 Btg128123.55 ± 0.09125.60 ± 0.111.017llama 3B Q4_02.08 GiB3.61 Btg128237.40 ± 1.03244.19 ± 0.711.029llama 1B Q4_0933.24 MiB1.50 Btg128519.27 ± 2.55538.75 ± 2.321.038llama 2-bpw TriLM45.84 MiB99.76 Mtg1281570.51 ± 49.671754.54 ± 64.751.117MetalM2-Maxmodelsizetestt/s (main)t/s (PR)Speedupllama 8B Q4_04.33 GiBtg12859.38 ± 0.0360.03 ± 0.031.011llama 3B Q4_02.08 GiBtg128107.61 ± 0.55108.74 ± 0.141.011llama 1B Q4_0933.24 MiBtg128225.92 ± 0.91230.26 ± 0.761.019llama 2-bpw TriLM45.84 MiBtg128520.46 ± 10.70545.46 ± 7.331.048从数据中可以清晰看到一个规律模型越小加速比越大。以 99.76 M 参数的 2-bpw TriLM 为例CUDA 侧达到约 1.117x 的加速而 8B 模型只有约 1.017x。PR 描述对此给出了解释图构建在 CPU 上、图计算在 GPU 上模型越小 GPU 计算时间越短图构建时间的占比就越高缓存图的收益也就越明显。同时 PR 也指出作者观察到的加速比小于 agray3 在 PR-8366 中报告的数值推测差异取决于 GPU 相对 CPU 的速度比。与 mainline llama.cpp 的同期对比PR 还以当日 mainlineafd9909a即 3942 提交为基准做了横向对比RTX-4080modelt/s (mainline)t/s (PR)Speedupllama 8B Q4_0122.48 ± 0.10125.60 ± 0.111.025llama 3B Q4_0233.04 ± 0.66244.19 ± 0.711.048llama 1B Q4_0505.63 ± 1.23538.75 ± 2.321.065M2-Maxmodelt/s (mainline)t/s (PR)Speedupllama 8B Q4_057.94 ± 0.3260.03 ± 0.031.036llama 3B Q4_0103.67 ± 0.21108.74 ± 0.141.049llama 1B Q4_0221.45 ± 1.31230.26 ± 0.761.039需要强调的是这些数据是 2024 年 10 月的瞬时快照仅代表当时该 PR 相对当日 mainline 的相对表现不构成对当前版本性能的承诺。源码级解读当前仓库中的图复用实现图缓存方案经过后续演进在 ik_llama.cpp 当前源码中已经发展为一套完整的图复用graph reuse机制主要实现在 src/llama.cpp 与 src/llama-context.h。1. 复用判定can_reuse_graph核心判定函数是llama_context::can_reuse_graphsrc/llama.cpp。它逐一检查当前批次能否安全复用上一次的图开关检查cparams.graph_reuse必须为 true无嵌入输入u_batch.embd为空即纯 Token 解码路径prompt 编码阶段不参与复用指纹匹配seq_fingerprint与model_state_hash与上次一致KV Cache 状态匹配KV Cache 当前长度kv_self.n、写入位置kv_self.head等与上次记录一致输出/批次形状匹配n_outputs、u_batch.n_tokens不变MTP多 Token 预测状态匹配mtp_op_type、mtp_step_idx、mtp_n_heads一致缓存拷贝可更新update_cache_copies()成功。其中seq_fingerprint由llama_ubatch_seq_fingerprintsrc/llama.cpp基于批次 Token 数、序列 ID、位置等信息计算得到model_state_hash则针对 DEEPSEEK4 等需要记录运行时压缩计划compression plan的架构计算src/llama.cpp。对于 hybrid/recurrent 类架构图会烘焙序列状态因此指纹检查尤为关键源码中还保留了IK_LEGACY_GRAPH_REUSE环境变量来关闭这一指纹检查、回退到旧的复用行为。2. 就地更新update_cache_copies复用图的关键在于参数就地更新。llama_context::update_cache_copiessrc/llama.cpp负责把每次 Token 解码后变化的 KV Cache 写入偏移view_offs、data指针重新指向当前kv_self.head对应的缓存槽位。从源码结构看它还针对不同架构做了专门处理DSA 索引器键缓存kr_l通过patch_dsa_cache_copies重新指向本次 KV 头部的写偏移src/llama.cppOpenPangu 架构对每层的openpangu_cache_copies批量更新偏移并区分 MTP 与非 MTP 两组拷贝src/llama.cpp对应的CacheCopy结构与各架构的缓存拷贝列表定义在 src/llama-context.h。如果这些cpy节点的形状或根张量与当前 KV Cache 不再匹配函数返回 falsecan_reuse_graph随之拒绝复用并触发一次完整重建。3. 主循环中的决策点在解码主循环llama_decode_internal附近中决策逻辑位于 src/llama.cpp若can_reuse_graph返回 false执行reset_scheduler()、调用llama_build_graph完整重建图并通过ggml_backend_sched_alloc_graph重新分配缓冲区然后把新图连同当时的 KV/批次状态快照存入prevllama_context::Prev结构定义见 src/llama.cpp供下次复用若返回 true直接取用prev-graph跳过构建与分配立即进入调度执行阶段。代码中还保留了IK_PRINT_TIMING宏包裹的分段计时打印可用于观察prelude、sched_reset、build_graph、sched_alloc_graph各阶段耗时直观验证图复用省掉的开销。4. 配置入口图复用功能在 common/common.h 中默认开启bool graph_reuse true;并在 include/llama.h 的llama_context_params中暴露为graph_reuse字段标注为 EXPERIMENTAL。命令行控制方式见 common/common.cpp-gr, --graph-reuse启用图复用默认开启-no-gr, --no-graph-reuse禁用图复用。运行时可打印配置确认状态common/common.cpp日志中会出现graph_reuse: true # default: false之类的行。此外对应原 PR 的GGML_DISABLE_GRAPH_CACHING环境变量属于当时方案的开关方式在当前代码中相关环境变量演化为IK_LEGACY_GRAPH_REUSE用于关闭 hybrid/recurrent 架构下的序列指纹检查恢复旧复用路径见 src/llama.cpp。适用场景与限制从实现与数据可以归纳出该方案的适用边界收益随模型规模变化模型越小、单 Token GPU 计算时间越短图构建开销占比越高缓存收益越明显大模型收益相对有限只覆盖解码decode阶段u_batch.embd非空的 prompt 编码路径不参与复用对图结构稳定性敏感KV Cache 长度、输出数、Token 数、MTP 状态、SWA滑窗注意力窗口视图等任一变化都会触发重建KV Cache 出现压缩compaction时也需要重新计算窗口视图来判定混合/循环架构有额外检查这类架构的图烘焙了序列状态需要序列指纹一致才能复用。讨论中的工程启示PR 的讨论串还透露了该项目后续的工程方向。ikawrakow 在评论中提到CUDA 上量化矩阵乘法MMQ kernels的构建时间与显存占用问题认为与其堆叠更多量化 kernel不如像 Metal 实现那样交错执行反量化与矩阵乘法从而接近 MMQ 的性能、显著降低临时显存占用、并恢复正常的构建时间。这解释了该项目 CPU 优先、GPU 次之的定位也从侧面说明像图缓存这样用软件工程换取算力效率的优化正是项目持续追求的方向之一。小结图缓存/图复用方案通过缓存计算图结构 就地更新 KV Cache 参数省去了自回归解码中每个 Token 的计算图重建开销在小模型与 GPU 计算较快相对 CPU 构建的场景下收益最为显著。在当前仓库中它已固化为默认开启的graph_reuse机制由can_reuse_graph负责严格的复用判定、update_cache_copies负责参数就地更新并可通过-no-gr/--no-graph-reuse随时关闭。理解这条实现链路有助于你在实际部署中判断图复用对自身场景的收益也为阅读 llama.cpp 系推理栈的解码调度代码提供了切入点。【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价