ggml 简介

ggml 是一个用 C 和 C++ 编写的底层机器学习(ML)库,专门用于 Transformer 推理。它是包括 llama.cpp、whisper.cpp、ollama、Jan、LM Studio 和 GPT4All 在内的流行设备端 LLM 项目的底层引擎。

ggml 的核心优势

ggml 旨在实现极简和高效,因而非常适合在资源受限的硬件上部署。其主要优势包括:

  • 最小占用:核心库仅包含不到五个文件,编译后的二进制大小低于 1 MB。
  • 简化编译:只需 GCC 或 Clang 即可进行基本编译,无需 GPU 支持,避免了复杂的构建工具。
  • 广泛的硬件兼容性:支持 x86_64、ARM、Apple Silicon、CUDA 等硬件架构。
  • 内存效率:库尽量减少张量存储和计算的开销,并支持量化张量以降低内存使用并可能提升性能。

技术权衡

作为一个仍在积极开发的底层库,ggml 存在以下局限性:

  • 后端差异:并非所有张量操作在所有后端都受支持(例如,某个操作在 CPU 上可用,但在 CUDA 上不可用)。
  • 开发复杂度:使用 ggml 需要深入的底层编程知识。
  • 稳定性:由于仍在积极开发中,预期会有破坏性更改。

关键术语与概念

理解 ggml 需要熟悉若干提供细粒度性能控制的底层抽象:

  • ggml_context:用于存放张量、计算图以及可选数据的容器。
  • ggml_cgraph:表示后端将执行的操作顺序的计算图。
  • ggml_backend:执行计算图的接口,已有 CPU(默认)、CUDA、Metal、Vulkan 和 RPC 等实现。
  • ggml_backend_buffer_type:与特定后端关联的内存分配器(例如,分配 GPU 内存)。
  • ggml_backend_buffer:由 buffer_type 分配的缓冲区,可容纳多个张量的数据。
  • ggml_gallocr:用于在计算图中高效管理张量的图内存分配器。
  • ggml_backend_sched:调度器,可在多个后端之间分配计算(例如在 CPU 与 GPU 之间分割工作),并自动将不支持的操作分配到 CPU。

实现工作流

基本计算

对于诸如矩阵乘法等简单操作,ggml 的工作流包括:

  1. 为张量数据分配一个 ggml_context
  2. 创建张量并分配数据。
  3. 为该操作(例如 ggml_mul_mat)定义一个 ggml_cgraph
  4. 通过 ggml_graph_compute_with_ctx 执行计算。
  5. 获取结果并释放内存。

基于后端的计算

在使用特定硬件后端(如 CUDA)时,为确保高效的设备内存管理,流程更为细致:

  1. 初始化 ggml_backend
  2. 仅为张量元数据分配一个 ggml_context
  3. 创建张量元数据(形状和类型)。
  4. 分配一个 ggml_backend_buffer 将张量存储在设备上。
  5. 将张量数据从 RAM 复制到后端缓冲区。
  6. 创建 ggml_cgraphggml_gallocr 用于图的分配。
  7. 通过 ggml_backend_graph_compute 执行计算。
  8. 将结果张量从设备缓冲区复制回 RAM。

调试计算图

开发者可以检查 ggml_cgraph 以验证操作顺序。库提供 ggml_graph_print 将图输出到控制台,ggml_graph_dump_dot 可将图导出为 Graphviz dot 格式以进行可视化渲染。

Sources