尧图精选

ik_llama.cpp 的 CUDA MLA 加速之路:从 3K 次 Kernel 启动到一次 GEMV 的合并优化

🕒 发布时间:2026/9/19 21:41:52 📁 来源:尧图网络
ik_llama.cpp 的 CUDA MLA 加速之路从 3K 次 Kernel 启动到一次 GEMV 的合并优化【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cppMLAMulti-head Latent Attention多头潜在注意力是 DeepSeek-V2/V3/R1 等模型的核心注意力机制通过在 KV 缓存中仅存储低秩潜在向量来大幅压缩显存占用。然而在 ik_llama.cpp 项目早期CUDA 后端对 MLA 的适配并不理想在主分支上MLA 注意力的解码速度比标准注意力实现慢 15%20%。本文基于仓库 github-data/pull_requests/234 - Faster MLA on CUDA.md 记录的 PR #234完整还原问题的根源、两项核心优化手段以及实测性能收益并结合当前仓库源码展示 MLA 特性在 ik_llama.cpp 中的后续演进与使用方式。读者读完本文将能理解张量乘法被拆解为逐行 GEMV为何会成为 CUDA 上的性能灾难以及专用融合 kernel 如何将其一次解决。一、问题背景CUDA 后端为什么不喜欢MLA在 PR #234 提交2025-02-26之前ik_llama.cpp 的 CUDA 代码对 MLA 的支持效果很差。作者 ikawrakow 在 PR 描述中直言The CUDA code absolutely does not like MLA.在解码token generationTG阶段MLA 注意力比标准注意力实现慢约 15%20%。问题出在两处核心矩阵运算上wk_b × q_nope将不参与旋转位置编码的 query 部分投影到 key 空间wv_b × qkv_compressed将压缩后的 q/k/v 潜在向量投影到 value 空间。对于单 token 解码TG这两个操作需要执行形状分别为 $(N_h \times N_t \times K)$ 与 $(N_h \times 1 \times K)$ 的张量乘法。其中$N_h$ 为注意力头维度head size$N_t$ 为 KV 缓存中的 token 数量随上下文增长$K$ 为注意力头数。二、逐行拆解为什么慢得离谱问题不在于乘法本身的算力需求而在于执行方式。上述两个张量乘法在现有 CUDA 路径中被实现为 $K$ 次连续的 $(N_h \times N_t) \times (N_h \times 1)$ 矩阵-向量乘法GEMV——每个注意力头一次。更糟的是wk_b × q_nope的情况当q_nope在显存中不连续non-contiguous时每次 GEMV 还需要为每一行q_nope各拷贝一份到连续内存共 $K$ 次拷贝当wk_b是量化权重时对单行执行一次量化最后才执行真正的 GEMV。也就是说一次wk_b × q_nope实际触发了$3K$ 次 CUDA kernel 启动。在 GPU 上kernel 启动的调度开销远高于矩阵乘法本身的执行时间因此计算被启动开销完全淹没表现为极端的性能退化。这与当前仓库 ggml/src/ggml-cuda/dmmv.cu 中保留的dequantize_mul_mat_vec_q*_k系列逐行 kernel 所反映的实现路径一致——每行独立的量化、反量化与向量点积都是独立 kernel。三、PR #234 的两项核心优化PR #234 通过两个针对性改动消除了上述开销1. 专用融合 GEMV Kernel一次启动完成 K 个头的 GEMVPR 新增了一个专用 kernel将原本需要 $K$ 次启动的逐头 GEMV 合并为一次 kernel 启动。作者在描述中坦承这是a bit of a hack并计划后续与常规的ggml_cuda_op_mul_mat_vec_q实现合并但在当时should do for now够用就行。这一融合思路后来成为 ik_llama.cpp 一系列 MLA 优化的起点。2. 新增quantize_tensor_q8_1_cuda单次调用完成非连续张量的单行量化PR 同时新增了quantize_tensor_q8_1_cuda方法它专门处理非连续non-contiguous且只有单行single row的张量。这使qk_b × q_nope乘法中q_nope的量化能够以单次调用完成而不是逐行分别量化——彻底消除了第 1 节中描述的多余拷贝与重复量化启动。四、实测性能收益MLA 反超标准注意力两项改动合并后MLA 注意力在 CUDA 上的计算速度显著提升对IQ4_NL量化的 DeepSeek-Lite全部层在 GPU 上处理时TG-128 吞吐量提升31%对专家层在 CPU 上计算的混合hybrid推理获得约15%的加速。PR 文档给出了混合 CPU/GPU 推理下标准注意力与本次 PR 的 MLA 注意力随上下文长度变化的完整对比数据CPU 为 Ryzen-7950XGPU 为 RTX-4080模型为 deepseek2 16B IQ4_NL测试项t/s标准注意力t/sMLA本 PR加速比tg64pp12852.99 ± 0.0352.43 ± 0.040.989tg64pp25652.77 ± 0.0952.26 ± 0.070.990tg64pp51251.58 ± 1.1951.93 ± 0.101.007tg64pp102450.75 ± 0.5651.73 ± 0.071.019tg64pp204849.96 ± 0.2851.29 ± 0.051.027tg64pp409647.94 ± 0.5850.23 ± 0.051.048tg64pp819243.77 ± 0.3448.04 ± 0.041.098tg64pp1638437.76 ± 0.1544.62 ± 0.171.182数据揭示了一个清晰规律上下文越短MLA 与标准注意力差距越小约持平上下文越长MLA 的优势越明显。在 pp16384 时加速比达到 1.182接近 20%。作者总结MLA 在短上下文下已几乎与标准注意力持平且随上下文长度增长逐步反超——这正得益于wk_b × q_nope/wv_b × qkv_compressed的融合执行消除了逐头 GEMV 的启动开销。社区实测反馈PR 讨论区中用户 davidsyoung 在 2025-02-27 报告了在自己环境中的实测Full CUDA 15×RTX 3090、Q2_K 量化 MLA 模型配合转置 KV 缓存解码速度从12 t/s 提升到 17.25 t/s并且长上下文大 PP token下的速度衰减也更小。五、后续演进从融合 GEMV 到 FlashMLA 系列PR #234 不是 MLA 优化的终点而是这条技术路线上的重要一站。从仓库 README.md 的更新日志可以还原完整的演进脉络2025-02-09PR #188 首次为 DeepSeek 模型引入 MLA 支持2025-02-26PR #234本文主题在 CUDA 上大幅加速 MLA 的 GEMV 路径2025-02-27PR #235 实现 MLA 无转置缓存MLA without transposed cache2025-03-03PR #240 引入 FlashMLA——MLA 与 Flash Attention 的结合2025-03-08 / 03-09PR #243 / #247 分别实现更快的 FlashMLA CPU 与 CUDA 实现2025-03-12PR #265 允许 FlashMLA-2 在 CUDA 上使用 Q8_0 KV 缓存2025-03-17 / 03-21PR #253、#273 带来 FlashMLA-2 性能改进与最快的 CPU-only 推理2025-05-07PR #386 推出 FlashMLA-3需 Ampere 或更新的 NVIDIA GPU。README 中特别指出MLA 正是先在 ik_llama.cpp 出现、随后才进入上游 llama.cpp的代表性特性之一。这一判断也能从当前源码中得到印证在 ggml/src/ggml-cuda/fattn-new-mma.cu 中flash attention 的 MMA kernel 通过模板参数is_mla在DKQ 576 DV 512或DKQ 320 DV 256时自动启用为 MLA 的 K/V 共享数据布局做了特化ggml/src/ggml-cuda/fattn.cu 也包含针对无 fp16 MMA 的 Pascal 架构sm_60下 MLA absorbed head 尺寸 576/512 的 decode 路由处理。此外src/graphs/build_deepseek2.cpp 中实现了 DEEPSEEK2 架构的 per-rank attention 图并在注释中明确要求 MLA 架构使用-fa与-mla 1组合。六、如何在当前仓库中使用 MLAMLA 通过命令行参数-mla即--mla-use启用其参数解析位于 common/common.cpp第 1929 行附近最终写入上下文的mla_attn字段。启用 MLA 的典型示例./build/bin/llama-server --model /path/to/DeepSeek-V3.gguf \ --ctx-size 8192 \ -mla 2 \ -fa \ --n-gpu-layers 65 \ --host 127.0.0.1 --port 8080结合 common/common.cpp 与 src/llama.cpp 的实现使用时有几点需要特别注意-mla只对真正使用 MLA 的模型有效仓库 github-data/issues/306 - Confused by the -mla flag. What_s supported_.md 记录了同样的误用案例——在 DeepSeek-R1-Distill-Qwen-32B 这类蒸馏模型上开启-mla 2会导致段错误SIGSEGV原因在于蒸馏模型沿用底层模型Qwen、LLaMA-3 等的标准注意力根本没有 MLA 结构。据作者回复当时已知使用 MLA 的模型为 DeepSeek-V2/V3/R1/Lite 系列。通常需要与 Flash Attention-fa配合从 src/graphs/build_deepseek2.cpp 的约束可以看出MLA 架构在-sm graph等图模式下要求-fa开启且-mla 1否则会直接中止。mla_attn的不同取值对应不同执行路径从 src/llama.cpp 中llm_prepare_mla等逻辑可见mla 1与mla 1分别走不同的缓存与投影路径例如mla1不启用pp_opt而mla2/3在长 prompt 下会预物化wk_b的转置且mla 1要求非量化或特定缓存类型的配合。此外 ggml/src/ggml-cuda/cpy.cu 的注释提到mla2使用 Q8_0 缓存时依赖特定的数据搬运路径。七、总结PR #234 用两项精准的改动合并 GEMV 的专用 kernel 非连续单行张量的quantize_tensor_q8_1_cuda量化把 CUDA 上 MLA 解码从比标准注意力慢 15%20%扭转到了短上下文持平、长上下文反超近 20%pp16384 下 1.182 倍为后续 FlashMLA 系列奠定了性能基础。对开发者而言这个故事的核心启示是在 GPU 上kernel 启动开销往往比浮点运算本身更昂贵将逐行的细粒度 GEMV 融合为单次 kernel 启动比盲目优化算力更能带来数量级的有效收益。如果你想亲自验证可参照上文示例用-mla配合-fa在 DeepSeek 系列模型上运行并对比同上下文长度下 MLA 与标准注意力的 TG 吞吐曲线——长上下文场景下两者差距会随pp增长而逐步拉开。【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →