Appearance
Transformer 推理图 — 代码走读
src/llama-model.cpp — 推理图入口(调度器)
src/llama-model.cpp 本身只承担调度职责:它提供推理图入口 build_graph、纯虚的按架构分发方法 build_arch_graph,以及根据架构选择 RoPE 类型的 llama_model_rope_type 开关。各架构真正的 forward pass 计算图实现已搬到了 src/models/*.cpp——例如 LLaMA 在 src/models/llama.cpp,GPT-2 在 src/models/gpt2.cpp 等等。
计算图构建入口
入口是 llama_model::build_graph(const llm_graph_params &)(src/llama-model.cpp),它把真正的"按架构建图"交给纯虚方法 build_arch_graph——每个具体架构的模型类(如 llama_model_llama)各自 override 它,从而完成分发,不再用一个大 switch:
cpp
// src/llama-model.cpp (~2720) —— 真实入口
ggml_cgraph * llama_model::build_graph(const llm_graph_params & params) const {
std::unique_ptr<llm_graph_context> llm = build_arch_graph(params); // 多态分发
llm->build_pooling(...); // 池化层(分类/嵌入等场景)
llm->build_sampling(); // 后端采样层(若有)
llm->build_dense_out(...);
llm->res->set_outputs(params);
return llm->res->get_gf();
}
// src/llama-model.h (~766) —— 基类里是纯虚:
virtual std::unique_ptr<llm_graph_context>
build_arch_graph(const llm_graph_params & params) const = 0;
// src/models/llama.cpp (~99) —— LLaMA 架构的 override:
std::unique_ptr<llm_graph_context> llama_model_llama::build_arch_graph(
const llm_graph_params & params) const {
return std::make_unique<graph<false>>(*this, params); // graph<embed> 见下
}旧版本曾用
llm_build_graph(lctx, batch)+llm_build_llama(...)/llm_build_gpt2(...)这一组自由函数 + switch 来分发;这套命名现已不存在,改成了上面的build_graph/ 虚函数build_arch_graph模式。
LLaMA Forward Pass(教学示意)
下面是概念性伪代码,用以说明 LLaMA 一层 forward 的数据流,并非字面源码。真实实现是
llama_model_llama::graph<embed>这个llm_graph_context子类的构造函数(src/models/llama.cpp)。注意:归一化 / 注意力 / FFN 都是llm_graph_context的成员方法(build_norm/build_attn/build_ffn,声明于src/llama-graph.h),不是llm_build_*自由函数。下文为对照清楚,仍用简化的build_*写法。
cpp
// 概念示意:llama_model_llama::graph<embed>(model, params) 的主干
// (真实代码在 src/models/llama.cpp;下面省略 build_inp_* 等细节)
struct ggml_tensor * cur = build_inp_embd(model.tok_embd); // token embedding
for (int il = 0; il < n_layer; il++) {
// 2a. Attention 前 norm(真实:llm_graph_context::build_norm)
cur = build_norm(cur, layers[il].attn_norm);
// Q/K/V 投影(此处直接写 ggml_mul_mat 仅作示意,真实由 build_attn 内部封装)
struct ggml_tensor * Q = ggml_mul_mat(ctx, layers[il].wq, cur);
struct ggml_tensor * K = ggml_mul_mat(ctx, layers[il].wk, cur);
struct ggml_tensor * V = ggml_mul_mat(ctx, layers[il].wv, cur);
// 2b. RoPE(下面这行是教学示意:真实调用见 ggml/src/ggml.c 的 ggml_rope,
// 其参数顺序/形式请以源码为准,rope_type 由 llama_model_rope_type 选定)
Q = ggml_rope(ctx, Q, positions, n_rot, rope_type);
K = ggml_rope(ctx, K, positions, n_rot, rope_type);
// 2c. Attention + KV Cache(真实:llm_graph_context::build_attn / build_attn_mha)
cur = build_attn(Q, K_cache, V_cache, mask);
// 2d. Output projection + 残差
cur = ggml_mul_mat(ctx, layers[il].wo, cur);
cur = ggml_add(ctx, cur, ffn_inp);
// 2e. FFN (SwiGLU)(真实:llm_graph_context::build_ffn)
ffn_inp = cur;
cur = build_norm(cur, layers[il].ffn_norm);
cur = build_ffn(cur, layers[il]);
// 2f. 残差
cur = ggml_add(ctx, cur, ffn_inp);
}
// 3. Final norm + 4. Output projection
cur = build_norm(cur, model.output_norm);
cur = ggml_mul_mat(ctx, model.output, cur);src/llama-graph.cpp — 图构建辅助方法
llama-graph.h / llama-graph.cpp 定义了 struct llm_graph_context,它把归一化、FFN、注意力等"积木"实现为成员方法(不是自由函数,也没有 llm_ 前缀):build_norm(llama-graph.h:~1065)、build_ffn(:~1096)、build_qkv(:~1075,本窗口新增)、build_attn(多处重载,:~1194 起,共 7 个)以及多维注意力的 build_attn_mha(:~1180,本窗口新增 n_kv_max 形参驱动稀疏 FA)。各架构的 graph 子类在自己的构造函数里调用它们来搭图:
cpp
// llm_graph_context::build_norm 的概念示意(真实声明在 src/llama-graph.h:~966)
// 构建归一化层 (RMSNorm) —— 它是 llm_graph_context 的成员方法
struct ggml_tensor * build_norm(
struct ggml_tensor * cur,
struct ggml_tensor * weight) {
cur = ggml_rms_norm(ctx, cur, epsilon);
cur = ggml_mul(ctx, cur, weight);
return cur;
}关键函数索引
| 函数 / 方法 | 文件 | 说明 |
|---|---|---|
llama_model::build_graph | llama-model.cpp | 构建完整推理图的入口 |
llama_model::build_arch_graph | llama-model.h(纯虚) | 按架构分发给各模型子类 |
llama_model_llama::graph<embed> | models/llama.cpp | LLaMA 架构的 forward pass 计算图 |
llm_graph_context::build_attn | llama-graph.h | 注意力计算(成员方法,7 个重载) |
llm_graph_context::build_attn_mha | llama-graph.cpp:2591 | 多头注意力(新 n_kv_max 形参 → 稀疏 FA) |
llm_graph_context::build_qkv | llama-graph.h:1075 | 消费 --fuse-qkv 合并的 QKV 投影(新) |
llm_graph_context::build_ffn | llama-graph.h | FFN(SwiGLU,成员方法) |
llm_graph_context::build_norm | llama-graph.h | RMSNorm(成员方法) |
llm_graph_context::build_sampling | llama-graph.cpp:3764 | 后端多输出采样子图(新) |
llm_graph_context::add_fused_node | llama-graph.h:926 | 注册一条融合算子链 |
ggml_rope | ggml/src/ggml.c | RoPE 位置编码(另有新 setter ggml_rope_set_offset) |
融合算子链(fused node)
融合算子的构造现已从 llama-context.cpp 移入图层:llm_graph_context 持有一个 std::vector<llm_graph_fused_node>(llama-graph.h:~887,结构为 {llm_fused_op op; ggml_tensor * tensor; int il;}),通过 add_fused_node(...) 注册、get_fused_nodes() 取出,由上下文在执行时统一应用。这样「哪些算子可融合」的决策集中在图构建阶段,而非散落在 context 里。
cpp
// src/llama-graph.h
struct llm_graph_fused_node {
// 描述一条待融合的算子链(如 norm + mul、rope 拼接等)
// ...
};
// 在 llm_graph_context 中:
void add_fused_node(llm_graph_fused_node result);
const std::vector<llm_graph_fused_node> & get_fused_nodes() const;这是本窗口的图层面重构(
90e0f5cfc,#24646):融合构造逻辑从llama-context.cpp(-131 行)迁入llama-graph.{cpp,h},使图成为融合信息的唯一来源。
DeepSeek V4 / 稀疏注意力的图接线
DeepSeek 系列的稀疏注意力在图层面消费新算子与 indexer:
- lightning-indexer + 超连接 —
GGML_OP_LIGHTNING_INDEXER与DSV4_HC_PRE/COMB/POST在 dsv4 的 forward 图中被组装(src/models/deepseek4.cpp),实现 lightning indexer 检索与融合超连接 - KQ mask 统一 f16 — DeepSeek V4 把所有 KQ mask 改为 f16(
2ed3c1abb),并在使用 Flash Attention 时移除零注意力 bias - 稀疏 FA 经 op 参数 — 本窗口起稀疏注意力不再需要专用图分支:
build_attn_mha把n_kv_max写入 FLASH_ATTN_EXT 的 op_params,CUDA/Metal 按 mask 有限项做稀疏 K/V(8e93a9773/7bb0fc18f) - MSA indexer 走独立输入类 — MiniMax-M3 的稀疏注意力现经
llm_graph_input_attn_kv_msa(携带 indexer key cache 的目标下标self_k_idxs_idx)写入独立的 indexer cache 实例(见 KV Cache 代码走读;67d5978bb把 MSA 移出了基类 cache)