零、前言

随着 AI 技术的不断发展,各种前沿 AI 科技从科研领域转向实战,其中 Python 环境(PyTorch、Transformers)以其极其高的性能与较为简单的使用工作量成为了许多人的首选。

然而,当想要进一步 将 AI 集成进高性能的原生应用 时,Python 的 运行时环境、巨大的依赖包体积 和 全局解释器锁 (GIL) 往往会成为 性能瓶颈。

为实现 高性能原生 AI 集成 并提供易用的相关工具,llama.cpp 应运而生。从其名字就可看出,其使用 C++ 作为其底层语言,其 高性能运行框架 与 集成的大量可用脚本 使其成为了一个几乎万能的 AI 领域工具箱。

在此之前,我在将自然语言模型的 LoRA 微调模型转为 gguf 格式,并进行量化以供 Ollama 本地高效运行时已经体验到了其便利:只需要利用已有 Python 脚本即可迅速将 huggingface 格式转为 gguf;而编译后提供的 llama-quantize 工具则可以便捷地进行模型量化。

如今,为了为我假期设计的 “低需求智能体流水线” 进行 CPU 推理与并行优化,我考虑了多个框架,并最终选择了该框架进行集成。为此,我开启了本系列博客以记录我 从源码阅读到工程化落地的全过程。

一、什么是 llama.cpp?

llama.cpp 是由 Georgi Gerganov 开发的一个开源项目,其核心目标是:以最少的依赖、最强的性能,在本地环境中实现 LLM(大语言模型)的推理。

它的核心特点包括:

  • 纯 C++ 实现:没有任何 Python 依赖,也不需要安装大量的第三方库。

  • GGML 张量库:底层使用了专门为高性能计算而设计的 ggml 库,支持在各种硬件上加速(Apple Silicon, CUDA, AVX2 等)。

  • 极其轻量:编译产物是一个简单的库文件或可执行文件,非常适合嵌入到其他 C++ 项目中。

  • 量化支持:它是 GGUF 格式的开创者,能够将庞大的模型压缩到普通消费级硬件也能正常运行的规模。

二、为什么选择 llama.cpp 作为推理后端?

在选择 llama.cpp 作为我的推理后端之前,我物色了大量推理框架:vLLM、TensorRT-LLM、ONNX Runtime 等。最终选择了它,主要基于以下考量:

1. 工程化成本极低

如果你用过 TensorRT,你会发现环境配置是一场噩梦。而 llama.cpp 只需要一个编译器。

对于 C++ 项目,它可以轻松地通过 add_subdirectory(CMake) 或链接一个动态库来集成。

2. 极高的硬件普适性

它不仅支持 NVIDIA GPU,对 MacBook (Metal)、普通 Intel/AMD CPU 以及 Android 设备都有极致的优化。这对于需要多平台发布的后端服务至关重要。

3. 活跃的社区与模型支持

基本上任何新出的模型(Llama 3, DeepSeek, Qwen),在发布后的 24 小时内都会有 llama.cpp 的适配。

4. 接口简洁但底层

虽然它提供的 C API(llama.h)看起来很原始,但正因如此,它没有过多的封装抽象,给了开发者最大的自由度去控制显存、并发和 KV Cache。

三、源码阅读:从哪里开始入手?

面对一个快速迭代的开源项目,直接死磕每一行代码会让人丧失信心。

作为二次开发的起点,我选择了按照以下顺序“分层”阅读源码:

1. 项目结构

  • llama.h:这个文件定义了所有的公开接口,是整个库对外调用的核心。

  • examples/simple/simple.cpp:这是官方提供的最小推理示例,只有几百行代码,可以借此理清一个模型从加载到输出文字的完整闭环。

  • common/:这是官方为了方便写 demo 而做的 C++ 封装(如采样逻辑、参数解析)。虽然不是核心库的一部分,但极具参考价值。

2. 核心生命周期

我的数学很不好,所以我放弃了去理解底层的数学算子(那是 ggml 做的),而重点关注 llama.cpp 是如何管理推理流程的:

  1. 后端初始化(llama_backend_init)

  2. 模型加载(llama_load_model_from_file)

  3. 批处理组织(llama_batch)

  4. 解码预测(llama_decode)

3. 进阶黑盒

我暂时将这些内容的理解优先级下调,等到需要做并发或长文本优化时再进行研究:

  • KV Cache 管理:如何利用缓存复用已计算的 Token 信息。

  • 采样算法:llama_sample_token 及其周边的采样逻辑。

四、结语

llama.cpp 的代码风格非常老派且硬核——简洁、紧凑。它没有过度设计,每一行都在为性能服务。

在下一篇博客中,我将记录如何搭建开发环境,并手把手从头开始编译运行第一个 C++ 推理示例。我将从 examples/simple 入手,拆解那几百行代码背后的逻辑。