llama.cpp:推理原理与 C API
「本系列第 6/17 章」。上一章已经能编出 llama-cli / llama-server,并准备好一份 GGUF。本章把「输入文本变成下一段文本」收成一条 C API 心智模型:对外只认 include/llama.h,对内则是只读权重加一份不可共享的会话状态。
1. 自回归:每次只多一个 token
语言模型推理是自回归的:给定前缀,预测下一个 token,追加后再预测。用户看到的「逐字往外吐」,就是这个循环。Causal Attention 规定位置 i 只能看见 ≤ i;训练时可以对整段序列并行算 loss,推理时通常只要最后一个位置的 logits。没有 KV cache 时,每一步都要把前文重算一遍,复杂度按序列长度平方涨;有 cache 之后,decode 只算新 token 的 K/V,并读取历史。
2. Prefill 与 Decode 是同一扇门
工程上常把推理分成两段,但 llama.cpp 没有两套入口。Prompt 一次性送进模型叫 prefill:token 多、可并行、主要职责是填满 KV,通常不为中间位置采样。之后每次只送一个新 token,叫 decode:计算量小、延迟敏感,每步调用一次 llama_sampler_sample。
两端都调用 llama_decode。区别只在 llama_batch 的大小,以及 logits 标志。llama_batch_allocr 默认只给最后一个 token 打开 logits。把 prefill 理解成「第一次 decode」,后面的循环就顺了。首 token 延迟主要由 prefill 决定,用户感知的吐字速度才是 decode。
3. 两个对象,两种线程语义
可以把对象关系记成:llama_model 像只读程序,llama_context 像进程,llama_batch 是本次输入包。
llama_model 装着权重、词表和架构,加载后只读,可被多个会话共享。一份 GGUF 不必为每个用户复制一遍。llama_context 装着一次推理的运行时:llama_memory_t、ggml_backend_sched、logits / embedding 缓冲。llama_context 不是线程安全的。多线程服务应当一线程一份 context,或在外层串行化。llama_backend_init / llama_backend_free 同样不是线程安全的,进程里只做一次。
llama_decode 的返回值要当成控制流:
| 返回值 | 含义 |
|---|---|
0 |
成功 |
1 |
KV 已满,当前 batch 放不进去(可 defrag 后重试) |
-1 |
参数错误 |
-2 |
内部错误(图分配或计算失败) |
2 |
用户 abort |
返回 1 时可以清序列、缩短上下文或增大 n_ctx;返回 -2 则应停止,不要继续采样。
4. 最小 C 循环
加载仍是两阶段:gguf_init_from_file(no_alloc=true) → llama_model_loader → llama_model_create。对外则收成:
1 | llama_backend_init(); |
对照 examples/simple/simple.cpp。llama_model_params 里最常改的是 n_gpu_layers、use_mmap、split_mode。llama_context_params 里是 n_ctx、n_batch、n_ubatch、n_seq_max、n_threads、flash_attn_type、type_k / type_v。采样不要手写 argmax 凑合:用 llama_sampler_chain_init 串 greedy / top_k / top_p / temp / penalties / grammar。Embedding 模型把 pooling_type 设成 LLAMA_POOLING_TYPE_MEAN,再读 llama_get_embeddings_seq。LoRA 走 llama_adapter_lora_init + llama_set_adapter_lora。
KV 显存可以先按 2 × n_layer × n_ctx × n_head × head_dim × sizeof(dtype) 估算。7B、4096 上下文、FP16 的 K/V 往往就要 1–2 GB,这还没算权重。--cache-type-k q8_0 和 -fa on 是降 KV 与算子开销的第一组旋钮。
下一章《llama.cpp:Decode、KV Memory 与 Graph 复用》会打开 llama_decode 的内部:batch 如何被切开,Memory 如何按架构选型,以及为何逐步生成可以复用同一张计算图。
正在加载留言…