ik_llama.cpp 注意力矩阵乘法策略优化解析:从 GQA 分块 GEMM 到 N_t×N_h 点积重排的 CPU 长上下文加速方案 ik_llama.cpp 注意力矩阵乘法策略优化解析从 GQA 分块 GEMM 到 N_t×N_h 点积重排的 CPU 长上下文加速方案【免费下载链接】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 #218「Better strategy for attention matrix multiplications when generating tokens」展开深入解析自注意力模块中K*Q与V*softmax(K*Q)两类矩阵乘法在 token 生成TG场景下的内存访问瓶颈以及该项目如何通过点积重排与多线程分块策略在长上下文场景下获得显著吞吐提升。读者将掌握 GQA/MLA 注意力结构下 CPU 端生成性能优化的核心思路、实测基准方法llama-bench 参数解读与性能收益的量化评估方式。背景为什么注意力矩阵乘法会成为 TG 的性能瓶颈在 Transformer 解码阶段每一层自注意力需要完成两组核心矩阵乘法K*Q计算 Query 与缓存中所有 Key 的相似度得分V*softmax(K*Q)用归一化后的注意力权重对 Value 做加权求和在 ik_llama.cpp 中这两组乘法在计算图中的形状为(K x N_t x N_k) × (K x N_b x N_h)其中各维度含义如下符号含义K注意力头尺寸head sizeN_tKV 缓存中的 token 数量即上下文长度N_b当前批次中的 token 数量N_kKV 头K/V heads数量N_h总注意力头query heads数量在传统 llama.cpp 实现中这一张量乘法被拆分为N_h次连续的矩阵乘法每次形状为(K x N_t) × (K x N_b)关键问题在于token 生成TG阶段每次只解码一个 token即N_b 1。此时上述操作退化为N_h次矩阵-向量乘法GEMV。GEMV 是典型的**内存带宽受限memory bound**操作——计算量远小于数据搬运量吞吐上限取决于内存带宽而非浮点算力。更糟的是内存布局本身也不友好左侧矩阵即 KV 缓存中相邻行之间的 stride 不是行大小R而是N_k * R。这意味着按行读取缓存数据时会产生大跨度跳跃缓存命中率低下进一步放大了内存受限问题。上下文越长N_t越大这个瓶颈就越突出。PR #207 的前置铺垫GQA 场景下的分块 GEMM化在分析 PR #218 之前有必要先了解它的直接前身 PR #207仓库中对应文档为 github-data/pull_requests/207 - Faster CPU TG for GQA models.md。该 PR 解决了GQAGrouped Query Attention场景下的 GEMV 退化问题当N_h N_kGQA 结构且N_h可被N_k整除时PR #207 将乘法策略改为执行N_k次矩阵乘法每次形状为(K x N_t) × (K x N_h/N_k)其核心思想是把Q形状N x 1 x LL为 query 头数重排为N x L/Lkv x LkvLkv为 KV 头数从而将大量 GEMV 转变为更少的矩阵-矩阵乘法GEMM。GEMM 的计算密度远高于 GEMV能更好地利用 CPU 的 SIMD 指令与多线程并行能力。PR #207 实测数据显示这种策略在长上下文下收益显著在 Ryzen-7950XZen4上LLaMA-3.1-8BIQ4_XS量化的 TG 吞吐在pp8192时从 8.60 t/s 提升到 10.26 t/s约 1.19×而在内存带宽更高的 M2-Max 上pp8192时从 14.07 t/s 提升到 19.66 t/s约 1.40×。这印证了文档中的判断该优化在长上下文大 KV 缓存时收益最大因为上下文较短时K*Q与V*softmax(K*Q)在整体耗时中占比很小。PR #218 的核心创新N_h N_k场景下的点积重排PR #207 的方案依赖N_h N_k这一条件。但当N_h N_k例如 DeepSeek 的 MLA 注意力架构query 头数与 KV 头数相等时N_h/N_k 1分块 GEMM 策略不再成立问题回归为N_h次 GEMV。PR #218 为此提出了一种新的乘法组织方式执行N_t * N_h次点积dot products内层循环遍历N_h外层循环遍历N_t。在多线程环境下每个线程负责约N_t/M * N_h次点积M为线程数。这一重排的核心收益在于内存访问的连续性按新的循环次序读取 KV 缓存时数据是**顺序访问consecutive access**的而不是原来那种带N_k * Rstride 的跳跃式访问。顺序访问带来更高的内存吞吐与更好的缓存利用率——这正是内存受限操作最需要的东西。与源码结构的对应关系从当前仓库源码看PR #218 涉及的注意力计算路径在计算图构建层有清晰落点。DeepSeek 系列架构N_h N_k的 MLA 结构的注意力构建位于 src/graphs/build_deepseek2.cpp其中build_deepseek2_tp_attention第 9 行起负责张量并行-sm graph模式下的逐 rank 注意力build_deepseek2_layer_attention第 720 行起对应非 TP 的逐层注意力路径。两者在 src/graphs/build_bailingmoe3.cpp 中被按配置分别调用。这些函数最终经由 src/llama-build-context.h 中声明的llm_build_kv等工具函数组装出K*Q与V*softmax(K*Q)计算图节点其底层矩阵乘法由ggml引擎ggml/src中的ggml_mul_mat系列内核含 iqk 的iqk_mul_mat_4d专用实现执行。可以说PR #218 的策略最终体现为对这些内核在 TG 单 token 输入下的数据访问次序与并行切分方式的重新编排。注上述源码路径对应当前仓库的最新状态实际实现可能已随后续 PR 继续演进但注意力计算的核心结构与本 PR 描述的策略一脉相承。实测性能三平台、四类 KV 缓存类型的完整基准PR 作者使用 DeepSeek-Litedeepseek2 16B以IQ1_S量化以最小化模型体积在三种 CPU 平台上进行了 TG 吞吐测试测试条件为不使用 Flash Attention-fa 0从而强制走传统K*Q/V*softmax(K*Q)路径确保新策略被实际触发缓存类型覆盖fp16、Q8_0、Q8_KV并在 Zen4 上额外测试bf16的 K 缓存文档明确指出无 FA 时 V 缓存无法量化因此 type_k 即代表 KV 缓存量化方式测试用例格式为tg128ppN即先生成/处理Ntoken 的 promptpp再生成 128 个 tokentgN从 128 逐步放大到 16384用于观察上下文增长对优化收益的影响。AVX2Ryzen-5975WX16 线程fp16K 缓存测试用例t/smaint/sPRSpeeduptg128pp12840.39 ± 0.0342.76 ± 0.031.059tg128pp25637.51 ± 0.0041.37 ± 0.031.103tg128pp51232.31 ± 0.0138.63 ± 0.011.196tg128pp102426.64 ± 0.0134.28 ± 0.021.289tg128pp204819.82 ± 0.0027.81 ± 0.011.403tg128pp409613.60 ± 0.0120.57 ± 0.001.512tg128pp81928.38 ± 0.0013.71 ± 0.001.636tg128pp163844.77 ± 0.008.20 ± 0.001.719Q8_KVKV 缓存量化:测试用例t/smaint/sPRSpeeduptg128pp12842.11 ± 0.0042.74 ± 0.021.015tg128pp25640.26 ± 0.0241.66 ± 0.021.035tg128pp51237.32 ± 0.0139.94 ± 0.011.070tg128pp102432.04 ± 0.0036.32 ± 0.021.133tg128pp204826.42 ± 0.0131.48 ± 0.011.192tg128pp409619.04 ± 0.0124.04 ± 0.011.263tg128pp819212.44 ± 0.0016.25 ± 0.011.306tg128pp163846.88 ± 0.0010.23 ± 0.001.487Q8_0K 缓存测试用例t/smaint/sPRSpeeduptg128pp12842.77 ± 0.0143.70 ± 0.011.022tg128pp25641.07 ± 0.0042.23 ± 0.001.028tg128pp51238.53 ± 0.0140.34 ± 0.001.047tg128pp102433.90 ± 0.0137.18 ± 0.021.097tg128pp204827.15 ± 0.0231.71 ± 0.001.168tg128pp409619.88 ± 0.0024.76 ± 0.001.245tg128pp819213.03 ± 0.0116.89 ± 0.011.296tg128pp163848.03 ± 0.0010.12 ± 0.001.260要点在 AVX2 平台上fp16K 缓存时收益随上下文长度单调增长pp16384时达到约1.72×Q8_KV与Q8_0时收益相对温和约 1.26×1.49×这与量化缓存下内存带宽压力变化有关。NEONM2-Max CPU8 线程fp16K 缓存测试用例t/smaint/sPRSpeeduptg128pp12856.84 ± 0.0558.21 ± 0.051.024tg128pp25654.55 ± 0.0157.45 ± 0.071.053tg128pp51250.99 ± 0.0455.47 ± 0.111.088tg128pp102444.53 ± 0.4851.93 ± 0.011.166tg128pp204835.92 ± 0.0245.80 ± 0.021.275tg128pp409625.96 ± 0.0137.36 ± 0.001.439tg128pp819216.38 ± 0.1127.21 ± 0.031.661Q8_KV测试用例t/smaint/sPRSpeeduptg128pp12857.73 ± 0.2858.10 ± 0.651.006tg128pp25656.40 ± 0.2257.27 ± 0.021.015tg128pp51253.61 ± 0.4155.95 ± 0.311.044tg128pp102449.15 ± 0.1254.00 ± 0.031.099tg128pp204841.54 ± 0.1248.59 ± 0.141.170tg128pp409631.24 ± 0.0041.31 ± 0.031.322tg128pp819221.75 ± 0.0131.66 ± 0.011.456要点M2-Max 拥有更高的内存带宽TG 基线更快但算力相对较弱更接近纯粹的带宽受限场景因此fp16下pp8192时达到约1.66×与 PR #207 中M2-Max 上 GQA 加速比更高的观察一致——带宽越充裕、算力越弱的平台内存访问重排带来的收益越明显。Zen4Ryzen-7950X16 线程bf16K 缓存测试用例t/smaint/sPRSpeeduptg128pp12848.84 ± 0.0849.32 ± 0.311.010tg128pp25646.17 ± 0.2747.52 ± 0.601.029tg128pp51241.76 ± 0.1744.86 ± 0.141.074tg128pp102436.58 ± 0.3838.99 ± 0.131.066tg128pp204829.55 ± 0.0333.11 ± 0.151.120tg128pp409620.95 ± 0.1724.87 ± 0.251.187tg128pp819214.55 ± 0.4816.72 ± 0.131.149tg128pp163849.11 ± 0.0010.14 ± 0.001.113fp16K 缓存测试用例t/smaint/sPRSpeeduptg128pp12848.25 ± 0.4249.61 ± 0.411.028tg128pp25645.62 ± 0.0447.76 ± 1.061.047tg128pp51242.08 ± 0.2245.34 ± 0.051.077tg128pp102437.14 ± 0.2039.65 ± 0.001.068tg128pp204829.74 ± 0.2333.98 ± 0.051.142tg128pp409621.98 ± 0.0325.09 ± 0.051.141tg128pp819214.59 ± 0.0716.92 ± 0.031.160tg128pp163849.52 ± 0.0010.10 ± 0.001.061Q8_KV测试用例t/smaint/sPRSpeeduptg128pp12849.87 ± 0.1050.47 ± 0.211.012tg128pp25646.89 ± 0.5349.02 ± 0.161.045tg128pp51244.08 ± 0.4146.57 ± 0.251.056tg128pp102440.59 ± 0.0942.50 ± 0.021.047tg128pp204834.32 ± 0.0437.55 ± 0.181.094tg128pp409626.09 ± 0.9929.50 ± 0.061.131tg128pp819219.43 ± 0.3520.64 ± 0.041.062tg128pp1638411.48 ± 0.0013.03 ± 0.001.135Q8_0测试用例t/smaint/sPRSpeeduptg128pp12850.69 ± 0.1750.70 ± 0.021.000tg128pp25648.54 ± 0.1549.55 ± 0.121.021tg128pp51245.99 ± 0.1146.98 ± 0.031.022tg128pp102442.85 ± 0.0642.35 ± 0.050.988tg128pp204837.02 ± 0.1137.57 ± 0.031.015tg128pp409629.10 ± 0.0729.63 ± 0.001.018tg128pp819220.55 ± 0.0920.71 ± 0.121.008tg128pp1638412.91 ± 0.0013.06 ± 0.001.012要点Zen4 的收益曲线呈先升后降形态bf16下在pp4096附近达到峰值约 1.19×Q8_0下收益最弱接近 1.0×pp1024甚至出现 0.988× 的轻微回退。这说明新策略的收益与缓存量化类型、平台内存层次密切相关并非在所有组合下都单调提升。如何复现这类基准llama-bench 参数对照PR 中的tg128pp16384等测试用例可直接用仓库自带的基准工具 examples/llama-bench 复现。该工具支持三类测试prompt processingpp用-p指定、text generationtg用-n指定、两者组合pg用-pg pp,tg指定默认512,128。与 PR 测试条件对应的关键参数./llama-bench \ -m /path/to/deepseek2-16B-IQ1_S.gguf \ -pg 16384,128 \ # 与 tg128pp16384 对应 -ctk fp16 \ # K 缓存类型f16 / q8_0 / q8_kv / bf16Zen4 支持 -fa 0 \ # 关闭 Flash Attention强制传统 K*Q / V*softmax(K*Q) 路径 -t 16 \ # 线程数与平台对应 -r 5 # 重复次数结果输出平均 t/s 与标准差参数速查摘自 examples/llama-bench/README.md参数说明默认值-m, --model模型文件路径models/7B/ggml-model-q4_0.gguf-p, --n-prompt用于 pp 测试的 prompt token 数512-n, --n-gen用于 tg 测试的生成 token 数128-pg pp,tg组合测试先 pp 后 tg512,128-ctk, --cache-type-kK 缓存类型f16/q8_0/q8_kv/bf16 等f16-ctv, --cache-type-vV 缓存类型f16-t, --threads线程数16-fa, --flash-attn是否启用 Flash Attention0-r, --repetitions每项测试重复次数5-o, --output输出格式csv/json/md/sqlmd值得注意的两点PR 文档特意强调calculations are without FA so the change in tensor multiplication strategy is invoked——关闭 FA 是触发传统乘法路径的前提否则计算会被 Flash Attention 内核接管点积重排策略不生效无 FA 时 V 缓存无法量化文档原文因此测试中用type_k统一代表 KV 缓存量化类型。这与当前仓库中 K/V 缓存量化能力-ctk/-ctv的设计是呼应的。规律总结与适用前提综合三平台、四类缓存类型的完整数据可以得出以下可验证的规律收益随上下文长度增长N_t越大K*Q/V*softmax(K*Q)占 TG 总耗时的比例越高重排策略的绝对收益越大。短上下文pp128pp512下加速比普遍在 1.01.1× 之间长上下文pp8192下可超过 1.6×。内存带宽越充裕的平台收益越大M2-MaxNEON AVX2 Zen4与 PR #207 中M2-Max 加速比显著更高的结论方向一致。量化缓存压缩了优化空间fp16缓存下收益最明显Q8_KV、Q8_0等量化缓存本身改变了内存带宽压力分布收益相对温和Zen4 上Q8_0甚至接近无收益。适用前提该策略针对N_h N_k无 GQA 的架构如 DeepSeek MLA 类在 TG 单 token 场景下的乘法重排GQA 架构N_h N_k由 PR #207 的分块 GEMM 策略覆盖Flash Attention 开启时路径不同本优化不参与。结语PR #218 是 ik_llama.cpp 在 CPU 推理性能优化上的一次精细手术不增加任何浮点计算量仅通过改变矩阵乘法的数据访问次序与多线程切分方式N_t * N_h点积、内层循环遍历N_h、每线程N_t/M * N_h次点积、顺序内存访问就在长上下文 TG 场景下换来了最高约 1.7× 的吞吐提升。它与其前身 PR #207GQA 分块 GEMM一起构成了该项目让 CPU 上的 KV 缓存访问更高效的完整技术脉络也解释了 ik_llama.cpp 在长上下文 CPU 推理场景中性能优势的一个关键来源。对于关注推理引擎底层优化或需要在纯 CPU 环境下运行长上下文模型的开发者这套识别内存受限 GEMV → 重排访问次序 → 实测量化收益的方法论具有直接的参考价值。【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考