llama.cpp CPU 占用分析:400% 的 CPU 追踪到 ggml 热点函数
当 llama.cpp 在 CPU 上运行 LLaMA 2 模型时,它可以轻松消耗掉 400% 的 CPU 核心资源。这些 CPU 时间究竟花在了哪里?我们使用 OpenResty XRay 对一个正在运行量化 7B 模型的、未经修改的 llama.cpp 进程进行了实时剖析,并将 CPU 占用追踪到两条最主要的代码路径:ggml_compute_forward_mul_mat(通用矩阵乘法)和 ggml_vec_dot_q4_K_q8_K(k_quants.c 中的量化向量点积)。
本文将展示完整的剖析过程——从观察到高 CPU 占用开始,一直定位到具体的源文件和行号。
运行 llama.cpp 并观察到 400% 的 CPU 使用率
首先,编译 llama.cpp 时开启调试符号,以便剖析工具能够解析函数名和源码位置:
cd llama.cpp
make -j4 OPT="-g -O3"
然后使用量化后的 LLaMA 2 7B 模型运行 main 程序。LLaMA 2 是 Meta 公司开源的大语言模型。这里我们使用 Q4_K_M 量化版本,它将 7B 模型压缩到约 4 GB——小到足以通过 llama.cpp 完全在 CPU 上运行:
./main -m models/llama-2-7b-chat.ggmlv3.q4_K_M.bin -n 4096 -p "Linux"
其中 -n 4096 选项指定生成的 token 数,-p "Linux" 则是用来生成内容的提示词。
进程加载模型后开始生成文本。请注意输出中的 system_info 一行——它显示 n_threads = 4、AVX = 1、AVX2 = 1 以及 BLAS = 0,表示当前构建使用了 AVX2 SIMD 指令,但没有链接外部 BLAS 库:
在另一个终端中,top 命令显示 main 进程消耗了接近 400% 的 CPU——4 个核心全部被打满:
问题来了:llama.cpp 内部究竟是哪些 C++ 函数造成了这么高的 CPU 占用?
使用 OpenResty XRay 剖析 llama.cpp 的 CPU 占用
我们使用 OpenResty XRay 来回答这个问题。OpenResty XRay 可以通过动态追踪技术直接附加到正在运行的、未经修改的进程上并生成 CPU 火焰图——无需重新编译,也无需插桩。
在 OpenResty XRay 的 Web 控制台中,进入 Guided Analysis(引导式分析)页面,选择 High CPU usage(高 CPU 使用率)作为问题类型,然后从进程列表中选择 llama.cpp 的 main 进程。控制台显示该进程的 CPU 占用为 320.4%,并且完整的命令行清晰可见:
将应用程序类型设置为 C/C++,最大分析时间保持默认的 300 秒不变。点击 Start analyzing(开始分析)后,系统会持续执行多轮采样分析。对这个案例来说,两轮分析就足够了。停止分析后,系统会自动生成分析报告。
分析结果:最热的 C++ 代码路径
自动生成的报告识别出三条热点 C++ 代码路径,每条路径都标注了占总 CPU 时间的百分比和完整的调用链:
第一名:ggml_compute_forward_mul_mat——占 CPU 时间的 21.1%
最热的代码路径指向 ggml.c 中的 ggml_compute_forward_mul_mat。这是 ggml 库中的通用矩阵乘法(GEMM)内核——它是 Transformer 推理中注意力机制和前馈层背后的核心运算。而其上一级调用它的函数 ggml_graph_compute_thread 负责进行单个线程的计算:
点击 More(更多)展开详情,可以看到这条代码路径所依据的 CPU 火焰图。火焰图以可视化的方式呈现了完整的调用栈,越宽的框表示消耗的 CPU 时间越多:
OpenResty XRay 还会为这条代码路径自动生成解释说明和优化建议。它指出 ggml_compute_forward_mul_mat 计算的是两个张量的矩阵乘法,并给出了包括并行化、内存访问优化,以及使用 BLAS 或 LAPACK 等优化库在内的多种优化策略:
其中 BLAS 这条建议在这里尤其切中要害——回想一下前面 system_info 输出中的 BLAS = 0,这说明 llama.cpp 用的是自己实现的 GEMM,而不是经过优化的 BLAS 库。在编译时启用 OpenBLAS 或 Intel MKL,有望显著降低这条热点路径的 CPU 占比。
第二名:ggml_vec_dot_q4_K_q8_K——占 CPU 时间的 17.4%
第二热的代码路径指向 k_quants.c 中的 ggml_vec_dot_q4_K_q8_K:
这个函数计算两个量化向量的点积——K 采用 4 比特量化(q4_K),Q 采用 8 比特量化(q8_K)。量化点积正是 llama.cpp 能够在 CPU 上跑动 7B 模型的关键所在——它带来了速度和内存上的巨大节省:
从剖析报告定位到源代码
OpenResty XRay 会把每个热点函数映射回它的源文件和行号。把鼠标悬停在报告中 ggml_vec_dot_q4_K_q8_K 的函数框上,提示框会显示确切位置:llama.cpp/k_quants.c 文件的第 2608 行:
打开 k_quants.c,按照报告提示框中的指引跳转到第 2608 行:
我们可以看到这行代码使用了 C 语言的位运算来对数组的元素进行一些操作——从打包成 12 字节的 scales 表示中解出量化的缩放因子:
在状态栏中可以看到这行代码也确实在之前报告里提到的 ggml_vec_dot_q4_K_q8_K 函数中:
这种精确度——从一个正在运行的进程一路定位到消耗 CPU 的具体源码行——正是动态追踪型剖析工具在分析 LLM 推理这类计算密集型应用的 CPU 去向时的价值所在。
常见问题
llama.cpp 为什么消耗这么多 CPU?
llama.cpp 对大语言模型做推理时,主要的计算就是矩阵乘法和量化向量点积。在我们对 LLaMA 2 7B 运行过程的剖析中,CPU 占用第一名是 ggml_compute_forward_mul_mat——ggml 库中的通用矩阵乘法内核;第二名是 ggml_vec_dot_q4_K_q8_K,它计算两个量化向量的点积。这些运算本身就是计算密集型的,在 CPU 上做推理很容易跑满多个核心、达到 400% 的占用。
如何剖析 llama.cpp 的 CPU 占用?
一种方法是使用像 OpenResty XRay 这样基于动态追踪的剖析工具。它可以直接附加到正在运行的、未经修改的 llama.cpp 进程上并生成 CPU 火焰图。火焰图能精确揭示哪些 C++ 函数消耗的 CPU 时间最多——一直细化到源文件和行号——而无需修改或插桩正在运行的应用程序。
llama.cpp 中最热的 C++ 函数是哪些?
在我们对运行量化 LLaMA 2 7B 模型的 llama.cpp 的分析中,第一热点代码路径是 ggml_compute_forward_mul_mat(占 CPU 时间的 21.1%),由 ggml_graph_compute_thread 调用;第二热点路径是 k_quants.c(第 2608 行)中的 ggml_vec_dot_q4_K_q8_K(占 17.4%),它对量化数组元素做位运算。加上第三条路径(占 13.4%),这三条代码路径合计占据了一半以上的 CPU 时间。
关于 OpenResty XRay
OpenResty XRay 是一个动态追踪产品,它可以自动分析运行中的应用程序,以解决性能问题、行为问题和安全漏洞,并提供可行的建议。在底层实现上,OpenResty XRay 由我们的 Y 语言驱动,可以在不同环境下支持多种不同的运行时,如 Stap+、eBPF+、GDB 和 ODB。
关于本文和关联视频
本文和相关联的视频都是完全由我们的 OpenResty Showman 产品从一个简单的剧本文件自动生成的。
关于作者
章亦春是开源 OpenResty® 项目创始人兼 OpenResty Inc. 公司 CEO 和创始人。
章亦春(Github ID: agentzh),生于中国江苏,现定居美国湾区。他是中国早期开源技术和文化的倡导者和领军人物,曾供职于多家国际知名的高科技企业,如 Cloudflare、雅虎、阿里巴巴, 是 “边缘计算“、”动态追踪 “和 “机器编程 “的先驱,拥有超过 22 年的编程及 16 年的开源经验。作为拥有超过 4000 万全球域名用户的开源项目的领导者。他基于其 OpenResty® 开源项目打造的高科技企业 OpenResty Inc. 位于美国硅谷中心。其主打的两个产品 OpenResty XRay(利用动态追踪技术的非侵入式的故障剖析和排除工具)和 OpenResty Edge(最适合微服务和分布式流量的全能型网关软件),广受全球众多上市及大型企业青睐。在 OpenResty 以外,章亦春为多个开源项目贡献了累计超过百万行代码,其中包括,Linux 内核、Nginx、LuaJIT、GDB、SystemTap、LLVM、Perl 等,并编写过 60 多个开源软件库。
关注我们
如果您喜欢本文,欢迎关注我们 OpenResty Inc. 公司的博客网站 。也欢迎扫码关注我们的微信公众号:
翻译
我们提供了英文版原文和中译版(本文)。我们也欢迎读者提供其他语言的翻译版本,只要是全文翻译不带省略,我们都将会考虑采用,非常感谢!
































