Appearance
采样、量化与部署 — 代码走读
src/llama-sampler.cpp — 采样器实现
采样器链架构
cpp
// 创建采样器链
struct llama_sampler * chain = llama_sampler_chain_init(
llama_sampler_chain_default_params());
// 添加采样器(顺序很重要!)
llama_sampler_chain_add(chain, llama_sampler_init_temp(0.8f));
llama_sampler_chain_add(chain, llama_sampler_init_top_k(40));
llama_sampler_chain_add(chain, llama_sampler_init_top_p(0.95f, 1));
llama_sampler_chain_add(chain, llama_sampler_init_dist(LLAMA_DEFAULT_SEED));
// 采样
llama_token token = llama_sampler_sample(chain, ctx, -1);采样器接口
每个采样器实现统一接口:
cpp
struct llama_sampler_i {
const char * (*name) (const llama_sampler * smpl); // 可为 NULL
void (*accept)( llama_sampler * smpl, llama_token token); // 可为 NULL
void (*apply) ( llama_sampler * smpl, llama_token_data_array * cur_p); // 必需
void (*reset) ( llama_sampler * smpl); // 可为 NULL
struct llama_sampler * (*clone) (const llama_sampler * smpl); // 可为 NULL
void (*free) ( llama_sampler * smpl); // 可为 NULL
// 后端采样接口(在 GPU/解码图上执行采样;本窗口随多输出采样扩展,include/llama.h:1285-1319):
bool (*backend_init) (llama_sampler * smpl, ggml_backend_buffer_type_t buft,
uint32_t n_outputs_max_per_seq);
void (*backend_accept)(llama_sampler * smpl, ggml_context * ctx, ggml_cgraph * gf, ggml_tensor * selected_token);
void (*backend_apply) (llama_sampler * smpl, ggml_context * ctx, ggml_cgraph * gf, struct llama_sampler_data * data);
void (*backend_set_input)(llama_sampler * smpl);
void (*backend_reset)(llama_sampler * smpl); // 重建采样图前清除内部状态
void (*copy_state) (const llama_sampler * src, llama_sampler * dst); // 复制可变状态,保留 dst 的图引用
};关键变化:apply 返回 void(不再返回 token),其入参是候选数组 llama_token_data_array * cur_p,而不是 (ctx, idx)。采样器就地修改 cur_p(排序、截断、掩码),最终 token 由链末的 dist 采样器或 llama_sampler_sample 取出。
llama_sampler_sample 的新快路径(本窗口)
llama_sampler_sample(src/llama-sampler.cpp:895)不再纯走 CPU:先查 llama_get_sampled_token_ith(ctx, idx)——若后端采样器已在该输出行产出 token,则调用 llama_sampler_accept(提交状态)并直接返回;否则用后端的部分输出(probs + candidates 或 logits + candidates)拼 cur_p,都没有才回落完整 CPU logits 跑链。链内还复用预分配的 cur 缓冲(llama-sampler.h:37)。
Top-P 实现
cpp
// Top-P: 就地修改候选数组 cur_p,保留累积概率 <= p 的 token(src/llama-sampler.cpp:~1549)
static void llama_sampler_top_p_apply(struct llama_sampler * smpl, llama_token_data_array * cur_p) {
auto * ctx = (llama_sampler_top_p *) smpl->ctx;
if (ctx->p >= 1.0f) {
return;
}
llama_sampler_softmax_impl(cur_p, false); // 就地 softmax 排序
// 计算累积概率,截断到 top-p,把被截断候选的 logit 置为 -INFINITY
// ...
}候选数组直接由调用方通过 cur_p 传入,并不存在 llama_sampler_get_candidates(...) 这个函数。
tools/quantize/ — 量化工具
量化工具 tools/quantize/quantize.cpp 的 main() 自身并不手写逐张量循环,而是调用库提供的单一入口 llama_model_quantize(...)(quantize.cpp:~634):
cpp
// quantize 工具核心(tools/quantize/quantize.cpp:~634)
llama_model_quantize_params params = llama_model_quantize_default_params();
// ... 根据 CLI 参数设置 params.n_threads / params.ftype / params.quantize_output_tensor 等 ...
if (llama_model_quantize(fname_inp.c_str(), fname_out.c_str(), ¶ms)) {
fprintf(stderr, "%s: failed to quantize model from '%s'\n", __func__, fname_inp.c_str());
return 1;
}逐张量的量化(按 GGML_TYPE_Q4_0、GGML_TYPE_Q5_K 等类型分发)是库内部实现,不在工具里手写。注意:枚举值 GGML_TYPE_Q4_0(ggml.h:~392)、GGML_TYPE_Q5_K(ggml.h:~403)确实存在,但并不存在 ggml_quantize_q4_0() / ggml_quantize_q5_K() 这样的公开函数。
tools/server/ — HTTP 服务器
注意:
app/llama.cpp现在提供统一入口,llama serve子命令会分发到 server 的 main 函数。
cpp
// server 核心循环(tools/server/server-http.cpp:~116 创建 httplib::Server)
int main(int argc, char ** argv) {
// 1. 解析参数、加载模型
// 2. 创建 HTTP 服务器 (httplib::Server),存于 pimpl->srv
// srv = std::make_unique<httplib::Server>(); // server-http.cpp:~116
// 3. 注册 API 路由(通过统一封装注册:路径 ↔ 处理器名有集中映射表,
// 如 "/v1/chat/completions" <-> "chatCompletions",见 server-http.cpp:~672/726)
// 4. 启动事件循环
pimpl->srv->listen("0.0.0.0", port);
}注意创建语句是 srv = std::make_unique<httplib::Server>()(早期版本是 pimpl->srv->Post(...) 直接逐条注册;现在路由经集中映射表分发)。路由处理最终分发到具体实现,例如 handle_completions_impl(server-context.cpp:~4257)与 handle_embeddings_impl(server-context.cpp:~5390)—— 并不存在 handle_chat_completion / handle_completion 这些名字。httplib 头本窗口起从 tools/server/httplib.h 迁移到 vendor/cpp-httplib/(拆成 .h + .cpp 两个文件)。
任务队列
cpp
// server 使用任务队列处理并发请求(tools/server/server-task.h:~136)
struct server_task {
int id = -1; // 由 server_queue 填充
size_t index = 0; // 多 prompt(batch)请求时使用
// ... cancel / 并行采样相关字段 ...
task_params params; // temperature、top_p、采样参数等
server_tokens tokens; // 已分词的 prompt
// ...
};
// 多个请求可以合并到一个 batch 中并行处理注意 server_task 的字段是 task_params params; 与 server_tokens tokens;,并没有 llama_token prompt 或 json params 这样的成员。
关键函数索引
| 函数 | 文件 | 说明 |
|---|---|---|
llama_sampler_chain_init | llama-sampler.cpp | 创建采样器链 |
llama_sampler_chain_add | llama-sampler.cpp | 添加采样器 |
llama_sampler_sample | llama-sampler.cpp | 从 logits 采样 token |
llama_sampler_init_top_k | llama-sampler.cpp | 创建 Top-K 采样器 |
llama_sampler_init_top_p | llama-sampler.cpp | 创建 Top-P 采样器 |
llama_sampler_init_temp | llama-sampler.cpp | 创建温度采样器 |