乐于分享
好东西不私藏

llama.cpp 源码解析(二):GGUF 格式与模型加载——从磁盘文件到内存张量

llama.cpp 源码解析(二):GGUF 格式与模型加载——从磁盘文件到内存张量

这是 llama.cpp 源码系列的第二篇。上一篇我们拆解了推理引擎 GGML,理解了张量、Arena、延迟执行、Galloc、KV Cache、线程模型的完整骨架。但还有一个问题没回答:模型文件本身是怎么设计的?从磁盘上的一个文件,到内存里一组可用的 ggml_tensor,中间经历了什么?

这就是本文要讲的内容。读完你会理解 GGUF 的二进制格式、mmap 为什么能做到"瞬间"加载、以及一个 HuggingFace 模型怎么转成 GGUF 用 llama.cpp 跑起来。


引言: 先说清楚这篇文章讲什么——以及为什么需要它

1 模型文件格式的本质——把"一堆数字"和"它们是什么"存进磁盘

抛开所有框架、所有工具,一个模型文件最底层到底存了什么?

两样东西:数据和元数据。

数据就是权重——训练结束后冻结下来的那几十亿个浮点数。元数据告诉你这些数字怎么用——它们按什么形状排列、属于哪一层、叫什么名字、模型总共多少层、用什么 tokenizer。

就这么简单。所有的格式之争,本质上都是在争"数据和元数据怎么组织、怎么打包、怎么读取"这三个问题的不同答案。

2 判断一个格式好不好的六个维度

拿到一个模型文件格式,可以从六个角度评价它:

维度
含义
为什么重要
安全性
打开文件时会不会执行恶意代码?
PyTorch 的 pickle 格式可以执行任意 Python,下载模型 = 运行未知代码
自描述
文件自己知不知道"我是什么"?
如果格式不自描述,就需要 config.json + tokenizer.json 等额外文件来补充
mmap 友好
能不能不经拷贝直接映射到内存?
决定了 100GB 模型是"瞬间"加载还是等几分钟
单文件
所有东西打包到一个文件?
单文件容易分发,多文件(如 safetensors 分片)需要索引
零依赖
读取格式需要什么运行时?
Python 依赖限制了部署场景(手机、嵌入式、WASM)
量化支持
能不能在文件内直接存储量化权重?
决定了是"加载后再量化"还是"加载即量化"

六个维度不是孤立的——一个维度的选择会影响其他维度。下面逐个格式看它们在这六个维度上的取舍。

3 格式一:PyTorch pickle(.pt / .pth / .bin)——最古老,也最危险

PyTorch 从 2016 年诞生起就用 Python 的 pickle 做序列化。Pickle 的设计初衷是"把任意 Python 对象存到磁盘,以后原样读回来"——包括类定义、函数、甚至 lambda。

怎么存的torch.save(model.state_dict(), "model.pt") → Python 的 pickle 模块把整个 state_dict(一个 OrderedDict,key 是层名,value 是 tensor)序列化成二进制。

问题在哪:pickle 的反序列化过程会执行 pickle 数据中的任意 Python 代码。这意味着一个恶意构造的 .pt 文件可以在你加载时删掉你的文件、窃取你的数据、安装后门。HuggingFace 早期大量使用这种格式,后来因为安全问题逐步迁移到 safetensors。

为什么推理不能用它:需要 Python + PyTorch 运行时(几百 MB)、不支持 mmap(必须一次性读到内存)、不支持量化(权重以 FP32/FP16 存储)、不是自描述的(需要额外代码定义模型类才能加载)。

4 格式二:Safetensors——安全替代 pickle

HuggingFace 在 2022 年推出了 safetensors。它的核心卖点是:纯数据,不能执行代码。

怎么存的:文件头 8 字节是 JSON 元数据的长度(uint64),后面紧跟一段 JSON 描述每个 tensor 的名字、形状、数据类型、在文件中的偏移量,再后面就是原生的 tensor 二进制数据。

Safetensors 文件布局:┌──────────────────────────────────────────────┐│ Header (8 bytes): JSON 部分的长度              │├──────────────────────────────────────────────┤│ JSON Metadata:                                ││   "token_embd.weight": {shape:[768,6400],     ││     dtype:"F16"offsets:[09830400]}       ││   "blk.0.attn_q.weight": {...}               ││   ...                                         │├──────────────────────────────────────────────┤│ Tensor Data (raw bytes):                      ││   [9.8MB of token_embd] [1.1MB of attn_q] ...│└──────────────────────────────────────────────┘

为什么它安全:JSON 不是图灵完备的——解析 JSON 不能执行代码。Tensor 数据是纯二进制,直接 memcpy 到目标地址。

为什么推理不能直接用:它只解决了"安全存储",没有解决"高效推理"。Safetensors 的元数据(JSON)在文件最前面,但这只是 tensor 级别的元数据——没有模型架构信息(多少层、什么 attention)、没有 tokenizer、没有推理超参数。而且它不支持量化格式内嵌——权重总是以 FP32/FP16/BF16 存储。

5 格式三:ONNX——跨框架的"通用语言"

ONNX(Open Neural Network Exchange)是微软和 Facebook 在 2017 年发起的。它的目标是:用 PyTorch 训练,用 TensorRT 推理,中间用 ONNX 做桥梁。

ONNX 用 Protobuf 定义了一个完整的计算图——不仅有权重,还有操作节点(Conv、MatMul、ReLU...)。这比 safetensors 更进一步:safetensors 只知道"有这么些 tensor",ONNX 知道"这些 tensor 怎么用"。

为什么 LLM 推理不用它:ONNX 是为视觉模型(固定输入大小、静态图)设计的。LLM 需要动态形状(batch 可变、序列长度可变)、KV Cache、autoregressive 循环——这些都是 ONNX 的弱项。加上 onnxruntime 的依赖链仍然很重。

6 格式四:AWQ / GPTQ——GPU 服务端的量化格式

这两个是专门为 GPU 服务端推理设计的量化格式,不是独立的文件格式——它们通常以 PyTorch checkpoint 或 safetensors 为载体,内部包含量化后的权重和量化参数。

  • AWQ
    (Activation-aware Weight Quantization):按激活值的大小分配量化精度,重要通道保留更高精度
  • GPTQ
    (GPT Post-Training Quantization):用 Hessian 矩阵逐层优化量化方案,质量接近原始

它们的问题:必须配合特定的推理引擎(vLLM、TGI、TensorRT-LLM),不能在 llama.cpp 中直接使用。因为它们的量化方案(per-channel scaling、分组量化)和推理逻辑是耦合的。

7 格式五:GGUF——专为本地推理而生

GGUF 的诞生动机很纯粹:前面四种格式没有一个适合"在用户的破笔记本上跑大模型"。

PyTorch pickle 不安全 + 需要 Python。Safetensors 安全但没有模型级元数据 + 需要 Python 库。ONNX 不适合动态 LLM。AWQ/GPTQ 绑定了 GPU 服务端推理引擎。

GGUF 的设计目标一句话:打开文件,一切就绪。 不需要 config.json,不需要 tokenizer.json,不需要 Python,不需要 GPU。一个文件,纯 C 读取,mmap 瞬间加载,量化格式内嵌,跨平台。

8 六维对比:一张表看懂定位

[图1:五种模型格式全景对比] — 五列卡片横向排列,每列标注格式名、诞生年份、核心特征。底部六维对比网格,GGUF 全部 ✓。时间线 2016→2023。

格式
安全性
自描述
mmap
单文件
零依赖
量化
诞生年份
定位
PyTorch .pt
❌ pickle
❌ 需代码
❌ Python
2016
训练 checkpoint
Safetensors
✓ 纯数据
△ Tensor级
否*
❌ 需库
2022
安全分发
ONNX
✓ 计算图
❌ runtime
2017
跨框架交换
AWQ/GPTQ
❌ 需引擎
2023
GPU 服务端
GGUF✓ KV+Tensor✓ 纯C✓ 40+2023本地推理

*Safetensors 通常分片存储(每个文件 < 5GB),需索引文件来拼接。但也可以不分片。

9 完整管线:一个模型从训练到部署的全路径

[训练框架:PyTorch / JAX / TensorFlow]   ↓ 输出训练好的权重PyTorch .pt  (pickle, 不安全)   ↓ 或直接存为Safetensors  (安全, 但仍是"通用数据容器")   ↓[格式转换 + 量化]  ← 这一步是本文的主题   ├─→ AWQ/GPTQ → vLLM/TGI        (GPU 服务器, 高并发)   ├─→ ONNX      → onnxruntime     (跨平台, 但不适合LLM)   └─→ GGUF      → llama.cpp       (本地/边缘, 零依赖)        ↓   llama-quantize (可选: FP16→Q4_K_M)        ↓   llama-cli -m model.gguf

GGUF 在这条管线中的位置很清楚:它是从"训练生态"到"部署生态"的桥梁。 上游的 safetensors 是训练框架的"出口格式",下游的 GGUF 是推理引擎的"入口格式"。中间的转换脚本(convert_hf_to_gguf.py)做两件事:把 safetensors 的 FP16 权重拷过来,把 HuggingFace config.json 和 tokenizer.json 转成 GGUF 的 KV 元数据。

10 这篇文章的写法

上一篇我们对着 8000 行 GGML 源码深度拆解,内容非常多。这篇会短一些——因为 GGUF 本身就是一个故意做得很简单的格式。它的设计哲学和 GGML 一脉相承:把事情做到刚好能用,然后停手。打开 ggml/include/gguf.h,前 31 行注释就是完整的文件格式规范。不需要 RFC 文档,不需要单独维护 wiki,源码即规范。

我第一次读 GGUF 相关代码的时候挺惊讶的——"?"总共就这两个文件:gguf.cpp(1556 行,读写实现)和 gguf.h(约 200 行,接口定义)。但越简单的设计越难做——你要在"足够用"和"不复杂"之间找到精确的平衡点。


第一章:GGUF 的二进制格式——五段结构

1.1 规范就在源码注释里

打开 ggml/include/gguf.h,前 31 行就是完整的文件格式规范。不用翻文档,不用查 wiki——源码注释就是规范本身。这很 GGML——把复杂的事情说得尽可能简单。

1. File magic "GGUF" (4 bytes)                    ← 文件头,固定 0x474755462. File version (uint32_t)                         ← 当前是 33. Number of ggml tensors in file (int64_t)         ← 有多少个权重张量4. Number of key-value-pairs in file (int64_t)      ← 有多少个元数据键值对5. For each KV pair:                                ← 遍历所有 KV 对   1. The key (string)   2. The value type (gguf_type)   3a. If array: type + count + binary of elements   3b. Otherwise: binary of value6. For each ggml tensor:                            ← 遍历所有张量   1. The tensor name (string)   2. The number of dimensions (uint32)   3. For each dimension: size (int64)   4. The tensor data type (int32)   5. The tensor data offset in tensor data blob (uint64)7. The tensor data binary blob (optional, aligned)  ← 实际权重数据

画成图就是五段:

┌──────────────────────────────────────────────┐│ 1. Magic  "GGUF"  (4 bytes)                  │├──────────────────────────────────────────────┤│ 2. Header                                     ││    version     = 3        (uint32)            ││    n_tensors   = 90       (int64)             ││    n_kv        = 33       (int64)             │├──────────────────────────────────────────────┤│ 3. KV Metadata (33 pairs, variable length)    ││    "general.architecture"  → "qwen3"          ││    "qwen3.block_count"     → 8                ││    "qwen3.embedding_length" → 768              ││    "tokenizer.ggml.tokens" → ["<|endoftext|>",││                               "<|im_start|>",││                               ...]            ││    ... (30 more)                               │├──────────────────────────────────────────────┤│ 4. Tensor Info Array (90 entries)              ││    [0"token_embd.weight"                     ││        dims=[7686400], type=F16, offset=0    ││    [1"blk.0.attn_norm.weight"                ││        dims=[768], type=F32, offset=9.8M       ││    [2"blk.0.attn_q.weight"                   ││        dims=[768768], type=F16, offset=...   ││    ... (87 more)                                ││   [89"output_norm.weight"                    ││        dims=[768], type=F32, offset=127.8M     │├──────────────────────────────────────────────┤│ 5. Tensor Data (aligned to 32 bytes)           ││    实际的权重值,按 Tensor Info 的 offset 定位    ││    总大小 = sum of all tensor sizes             │└──────────────────────────────────────────────┘

1.2 存储类型系统

GGUF 的 KV 元数据支持 12 种基本类型(ggml/include/gguf.h:53-68):

enum gguf_type {    GGUF_TYPE_UINT8   = 0,   // 无符号 8 位整数    GGUF_TYPE_INT8    = 1,    GGUF_TYPE_UINT16  = 2,    GGUF_TYPE_INT16   = 3,    GGUF_TYPE_UINT32  = 4,    GGUF_TYPE_INT32   = 5,   // 枚举用这个    GGUF_TYPE_FLOAT32 = 6,   // 浮点数    GGUF_TYPE_BOOL    = 7,   // 布尔值(存储为 int8)    GGUF_TYPE_STRING  = 8,   // 字符串(长度 uint64 + 内容)    GGUF_TYPE_ARRAY   = 9,   // 数组(类型 + 数量 + 元素列表)    GGUF_TYPE_UINT64  = 10,    GGUF_TYPE_INT64   = 11,   // 大整数    GGUF_TYPE_FLOAT64 = 12,};

字符串的存储方式是 string_length (uint64) 后跟 C 字符串(无 null 终止符)。这样读取时知道要读多少字节,不用扫描 null。

1.3 真实例子:MiniMind 的 GGUF 文件

用 llama.cpp 自带的 llama-gguf 工具看一眼真实的 MiniMind 文件:

$ llama-gguf minimind-f16.gguf r nHeader:  magic    = GGUF  version  = 3  n_tensors = 90  n_kv      = 33KV[0]:  general.architecture        = "qwen3"        ← 决定了构建器KV[1]:  general.type                = "model"KV[2]:  general.name                = "Minimind 3"KV[3]:  general.version             = "3"KV[9]:  qwen3.block_count           = 8              ← 层数KV[10]: qwen3.context_length        = 32768          ← 训练上下文KV[11]: qwen3.embedding_length      = 768            ← 嵌入维度KV[12]: qwen3.feed_forward_length   = 2432           ← FFN 维度KV[13]: qwen3.attention.head_count  = 8              ← Q 头数KV[14]: qwen3.attention.head_count_kv = 4            ← KV 头数 (GQA!)KV[15]: qwen3.rope.freq_base        = 1000000.0      ← RoPE 频率KV[19]: general.file_type           = 1              ← 1 = F16KV[21]: tokenizer.ggml.model        = "gpt2"         ← tokenizer 类型KV[22]: tokenizer.ggml.pre          = "qwen2"        ← pre-tokenizerKV[23]: tokenizer.ggml.tokens       = ["<|endoftext|>", ...] ← 6400 tokensTensor[0]:  name="token_embd.weight",       dims=[768,6400], type=F16, offset=0Tensor[1]:  name="blk.0.attn_norm.weight",  dims=[768],     type=F32, offset=9.8M...Tensor[85]: name="blk.7.attn_output.weight", dims=[768,768], type=F16, offset=125MTensor[89]: name="output_norm.weight",      dims=[768],     type=F32, offset=127.9M

33 个键值对告诉 llama.cpp 关于这个模型的一切。有了它们,不需要任何额外的配置文件。

1.4 为什么 Tensor Info 在数据区前面

这不是随意的。回想上一篇讲的 mmap:GGUF 文件被 mmap 映射后,程序先读取前面的元数据区(几十 KB),这些页面被加载到物理内存。然后它知道了每个张量在文件里的精确偏移——token_embd.weight 从字节 X 开始、blk.0.attn_q.weight 从字节 Y 开始。

这些偏移量直接作为 tensor->data 的地址,不需要 fread。当程序第一次访问某个张量的数据时,操作系统触发缺页中断,按需加载对应的 4KB 页。 如果某些层的权重在当前推理中没有被用到(比如只跑前几层的情况),它们对应的页面永远不会被加载。

GGUF 的格式设计唯一的指导原则就是"怎么让 mmap 用得最爽"。

[图2:GGUF 文件五段二进制结构] — 纵向展示 Magic → Header → KV 元数据 → Tensor Info → Tensor Data 的完整布局,标注每段大小和用途。

[图3:Safetensors vs GGUF 格式布局对比] — 看完 GGUF 的五段结构后,这里并排对比 Safetensors(Header→JSON→Data)和 GGUF(Magic→Header→KV→TensorInfo→Data)。核心差异:Safetensors 只存张量级元数据,GGUF 内嵌模型架构、超参数、词表、对话模板。


第二章:GGUF 的内存表示——gguf_context

2.1 两个数据结构:gguf_context 和 gguf_tensor_info

当 gguf_init_from_file 被调用时,它创建两个 C++ 结构(ggml/src/gguf.cpp:217):

struct gguf_context {    uint32_t version = GGUF_VERSION;    std::vector<struct gguf_kv> kv;              // 所有 KV 键值对    std::vector<struct gguf_tensor_info> info;   // 所有张量的信息    size_t alignment = GGUF_DEFAULT_ALIGNMENT;   // 对齐要求(默认 32)    size_t offset    = 0;                        // 数据区在文件中的起始位置    size_t size      = 0;                        // 数据区的总字节数    void * data = nullptr;                       // 数据区指针(mmap 地址 或 malloc)};

gguf_kv 存一个键值对的类型和值,gguf_tensor_info 存一个张量的名字、形状、ggml 类型、在数据区中的偏移量。

2.2 读取流程:gguf_init_from_file

这个函数是 GGUF 的核心实现(ggml/src/gguf.cpp,约 200 行)。把它拆成四步,每一步都很直白:

步骤 1:验证"这真的是个 GGUF 文件"。 读前 4 个字节,看是不是 GGUF(0x47475546)。如果不是——直接报错。然后读版本号和你最关心的两个数字:文件里有几个张量、几个 KV 对。

uint32_t magic   = read_u32(file);   // 必须是 "GGUF"uint32_t version = read_u32(file);   // 当前是 3uint64_t n_tensors = read_u64(file); // 例如 90(MiniMind)uint64_t n_kv      = read_u64(file); // 例如 33

步骤 2:读所有 KV 元数据。 循环 n_kv 次,每次读一个 key(字符串)+ type(枚举)+ value(按 type 决定长度)。GGUF 用 type 字段告诉读取者"接下来多少字节、怎么解析"——这是自描述格式的核心。

for (int i = 0; i < n_kv; i++) {    string key   = read_string(file);        // "general.architecture"    int32_t type = read_i32(file);           // GGUF_TYPE_STRING = 8    // 根据 type 决定怎么读 value    switch (type) {        case GGUF_TYPE_STRING: value = read_string(file); break;  // "qwen3"        case GGUF_TYPE_UINT32: value = read_u32(file);   break;  // 8        case GGUF_TYPE_ARRAY:  /* 先读元素类型+数量,再逐个读 */ break;        // ...    }    ctx->kv.push_back({key, type, value});}

步骤 3:读所有 Tensor Info。 循环 n_tensors 次,每次读名字、维度数组、ggml 类型、在数据区的偏移量。这一步读完,每个张量的"身份证"就齐了,但数据还躺在磁盘上。

for (int i = 0; i < n_tensors; i++) {    string name     = read_string(file);         // "blk.0.attn_q.weight"    uint32_t n_dims = read_u32(file);            // 2    int64_t ne[4] = {1,1,1,1};    for (int d = 0; d < n_dims; d++)        ne[d] = read_u64(file);                  // [768, 768]    ggml_type type = (ggml_type)read_i32(file);  // GGML_TYPE_F16    uint64_t offset = read_u64(file);            // 在数据区偏移 1.1MB    ctx->info.push_back({name, ne, type, offset});}

步骤 4:处理数据区。 此时文件指针恰好在数据区开头。记录下这个位置和数据区大小,然后用 mmap 或 malloc+fread 建立访问通道。数据区本身可能非常大(几十 GB),但在 mmap 模式下,这一行执行完函数就返回了——数据没有真正加载到物理内存。


第三章:llama_model_loader——从 gguf_context 到 llama_model

3.1 加载器做什么

src/llama-model-loader.cpp (1695 行) 负责把 gguf_context 转换成 llama_model。上一篇我们学过 llama_model 的结构——它包含所有层的权重张量引用。

转换步骤分四步:

第 1 步:验证和读取超参数。 从 KV 元数据中提取架构信息:

// llama-model-loader.cpp 内部std::string arch = gguf_get_val_str(meta, "general.architecture");// arch = "qwen3"int n_layer  = gguf_get_val_u32(meta, "qwen3.block_count");       // 8int n_embd   = gguf_get_val_u32(meta, "qwen3.embedding_length");  // 768int n_head   = gguf_get_val_u32(meta, "qwen3.attention.head_count"); // 8int n_head_kv = gguf_get_val_u32(meta, "qwen3.attention.head_count_kv"); // 4// ... 十几个超参数全部从 KV 对中提取

第 2 步:确定模型架构。general.architecture = "qwen3" 触发架构匹配链:

"qwen3" → llama-arch.cpp → LLM_ARCH_QWEN3 → llama-model.cpp → 选择 builder

在 llama-arch.cpp (906 行) 中有一个巨大的架构特征表,记录了每种架构的"能力":

// llama-arch.cpp 内部(简化)static const std::map<llm_arch, llm_arch_features> ARCH_FEATURES = {    {LLM_ARCH_LLAMA,  {.has_attention=true, .has_rope=true, ...}},    {LLM_ARCH_QWEN3,  {.has_attention=true, .has_rope=true,                       .has_qk_norm=true, .has_swiglu=true, ...}},    {LLM_ARCH_MAMBA,  {.has_ssm=true, ...}},    // ... 130+ 个架构};

第 3 步:创建张量壳子。 为每个权重在 model_ctx 里创建 ggml_tensor。llama.cpp 用一套简洁的命名规则来组织这些张量:

模型级(不属于任何层):  token_embd   — 词嵌入矩阵,把 token ID 映射成 768 维向量  output_norm  — 输出前的 RMS 归一化  output       — 输出投影(lm_head),把 768 维映射回 6400 维词表每层(blk = block,即 Transformer 层):  blk.{i}.attn_norm    — 注意力前的归一化  blk.{i}.attn_q       — Q 投影权重(768×768)  blk.{i}.attn_k       — K 投影权重(384×768,GQA 的 KV 头只有 Q 的一半)  blk.{i}.attn_v       — V 投影权重(384×768)  blk.{i}.attn_output  — 注意力输出投影(768×768)  blk.{i}.ffn_gate     — FFN 的门控权重(SwiGLU 的 gate 分支)  blk.{i}.ffn_up       — FFN 的上投影(SwiGLU 的 up 分支)  blk.{i}.ffn_down     — FFN 的下投影(把 2432 维压回 768 维)

代码里通过一个 create_tensor lambda 批量创建:

// llama-model.cpp:3074auto create_tensor = [&](tensor_name, shape, flags) -> ggml_tensor * {    // 在 model_ctx (Arena, no_alloc=true) 里分配 ggml_tensor 结构体    return ml.create_tensor(hparams, ..., tensor_name, shape, flags);};for (int il = 0; il < n_layer; il++) {    layers[il].wq = create_tensor(TN::ATTN_Q, {n_embd, n_embd}, il);    layers[il].wk = create_tensor(TN::ATTN_K, {n_embd_k_gqa, n_embd}, il);    // ... 每个层创建 ~13 个权重张量}

我第一次看到这个循环的时候,脑子里闪过上一篇的 llm_build_qwen3——那个 builder 也是用同样的循环在 graph_ctx 里创建计算图节点。一个负责"把权重从文件读到内存",一个负责"把权重变成计算图"。两人用同一个循环结构,各司其职。很整齐。

90 个 ggml_tensor 壳子创建完毕,全部 data=NULL

第 4 步:分配 backend_buffer,落实数据。 这是上一篇第二章 2.3 讲过的逻辑:

// llama-model.cpp:8187buf = ggml_backend_alloc_ctx_tensors_from_buft(model_ctx, buft);// → 遍历 model_ctx 中所有 tensor// → 分配 122MB 连续 buffer(CPU malloc / GPU cudaMalloc / mmap)// → 逐个设置 tensor->data = buffer_base + offset// → 逐个设置 tensor->buffer = buf// → 如果 mmap:data 指针直接指向 GGUF 文件的虚拟映射地址

3.2 分片文件的加载

FAT32 文件系统单文件最大 4GB。几十 GB 的大模型必须拆成多个分片发布。GGUF 的分片命名遵循 NAME-%05d-of-%05d.gguf 格式:

model-00001-of-00004.gguf  (0~4GB)model-00002-of-00004.gguf  (4GB~8GB)model-00003-of-00004.gguf  (8GB~12GB)model-00004-of-00004.gguf  (12GB~16GB)

llama.cpp 加载分片的过程(llama-model-loader.cpp 内部):

1. 检测文件名中的 -%05d-of-%05d 模式 → 推断有 4 个分片2. 打开第一个分片 → 读取 Header + KV 元数据   (后续分片的 Header 也被读取做校验,但 KV 元数据只从第一个文件取)3. 读取所有分片的 Tensor Info   → 每个分片有自己的 Tensor Info,但 n_tensors 总数相同   → 加载器把同名的 Tensor Info 合并:offset 加上前面分片的数据累积偏移4. mmap 数据区   → 每个分片独立 mmap,拼接成逻辑上连续的地址空间   → 或 malloc 一份大 buffer,把各分片数据依次 memcpy 进去关键约束:一个张量的数据不能跨分片边界。GGUF 的分片是对齐到张量边界的——不会有张量的前半段在分片 N、后半段在分片 N+1 的情况。

加载器用 llama_get_list_splits 自动检测和拼接(llama-model-loader.cpp:78)。这也是为什么 GGUF 的 Tensor Info 里有 offset 字段——对单文件来说它是"文件内偏移",对分片来说它是"合并后的逻辑偏移"。


第四章:mmap 深度配合——GGUF 的核心设计哲学

4.1 传统做法 vs mmap——差了几个数量级

传统 fread 做法fopen → malloc(100GB) → fread(100GB) → fclose。对于 100GB 的模型,你需要 100GB 空闲物理内存。fread 是把整个文件从头到尾拷贝到 malloc 出的 buffer 里——100GB 的数据搬运,即使 SSD 读写 3GB/s 也要半分钟以上。

mmap 做法fopen → mmap(建立映射) → fclose。核心是这一行系统调用:

void * data = mmap(    NULL,             // 让内核选地址    file_size,        // 映射整个数据区    PROT_READ,        // 只读    MAP_PRIVATE,      // 私有映射(修改不影响原文件)    fd,               // 文件描述符    data_offset       // 从文件的第 data_offset 字节开始映射);

这行代码不拷贝任何数据。它只是在进程的页表里建了一条记录:"虚拟地址 0x7f0000000000 到 0x7f0XXXXXXXXX,对应磁盘上 model.gguf 的第 data_offset 到末尾"。操作系统返回"好的,已记录"——函数就返回了。

缺页中断怎么工作? 当程序第一次通过 tensor->data 访问某个权重值(比如 blk.0.attn_q.weight->data[0]):

CPU: 我要读虚拟地址 0x7f0000100000 的 4 字节MMU: 这个地址对应的物理页还没加载 → 触发 page fault (中断 #14)内核: 从页表查出 → 对应 model.gguf 文件偏移 1.1MB      → 在物理内存找一页空闲的 4KB      → 发起磁盘 I/O:读 model.gguf 的 1.1MB~1.1MB+4KB      → 把数据写入物理页      → 更新页表:虚拟地址 0x7f0000100000 → 物理页 X      → 返回用户态CPU: 重试刚才的读操作 → 成功读到数据程序: 完全不知道刚才发生了什么

整个过程中,程序看到的只是"读了一个内存地址,拿到了值"。内核在背后默默做了磁盘 I/O。

[图4:mmap 缺页中断五步机制] — 三层结构(用户态/内核/物理内存),展示 ①mmap 映射 → ②程序访问 → ③缺页中断 → ④内核处理 → ⑤数据返回的完整流程。

4.2 mmap 和 GGUF 的配合——设计即优化

GGUF 的五段结构让 mmap 的效果最大化:

Header (16B) + KV (几KB) + Tensor Info (几KB)  → 必须立即读,但只有几十 KBTensor Data (122MB~100GB)                       → mmap 映射,按需加载

程序只需要立刻读 Header + KV + Tensor Info(总共几十 KB),然后对数据区做 mmap。推理时访问到哪个张量,操作系统加载哪个页面。没访问到的永远不占物理内存。

如果 GGUF 把元数据放在数据区后面会怎样? 那读取元数据前,必须先跳过整个数据区——要么 fread 整个文件(太慢),要么 seek 到末尾(不支持 mmap 流式读取)。GGUF 的"元数据在前"布局是专门为 mmap 设计的。

多轮对话的叠加优化:第一轮推理时,每层的权重按顺序被访问,操作系统逐页加载。第二、第三轮时,这些页面已经在前几轮中被加载过了,直接从操作系统的 page cache(页缓存)命中——没有磁盘 I/O。操作系统会自动把"热"页面留在内存,"冷"页面换出。用户感受到的效果是:"第一轮稍微慢一点,后面越跑越快。" 这是 mmap + OS page cache 自带的免费午餐。

4.3 GGUF 的默认对齐:32 字节

GGUF 数据区默认 32 字节对齐(GGUF_DEFAULT_ALIGNMENT = 32)。这个数字不是随便选的——对应 AVX/AVX2 的 256 位寄存器宽度,也是 DDR4/DDR5 内存总线的 burst 大小。对齐的张量数据可以一次内存访问完成,不对齐的需要两次。而且 32 是 4096(页大小)的约数,确保张量不会跨页边界——跨页边界的张量会触发两次缺页中断,而不是一次。


第五章:从 HuggingFace 到 GGUF——转换工具的工作原理

5.1 convert_hf_to_gguf.py 做了什么

这个 13423 行的 Python 脚本是 GGUF 生态的关键。它读 HuggingFace 格式的模型目录,输出 GGUF 文件:

HuggingFace 目录:├── config.json        → 读出架构名、层数、维度 → 写入 GGUF 的 KV 区├── tokenizer.json     → 读出所有 token → 写入 GGUF 的词表 KV 对├── model-00001-of-N.safetensors → 读出所有权重 → 写入 GGUF 的数据区└── ...           ↓ convert_hf_to_gguf.pyGGUF 文件:├── Header (magic + version + n_tensors + n_kv)├── KV Metadata (33 对: architecture=qwen3, block_count=8, ...)├── Tensor Info (90 个: name, shape, type, offset)└── Data (122MB 对齐后的权重值)

5.2 支持的架构

在 convert_hf_to_gguf.py 内部,每种架构是一个 ModelBase 的子类。以 Qwen3 为例:

@ModelBase.register("Qwen3ForCausalLM")classQwen3Model(TextModel):    model_arch = gguf.MODEL_ARCH.QWEN3

@ModelBase.register 装饰器把 HuggingFace 的类名("Qwen3ForCausalLM")和 GGUF 的架构名(QWEN3)绑定。当脚本读到一个 HuggingFace 模型的 config.json 里 "architectures": ["Qwen3ForCausalLM"],自动匹配到这个类。

5.3 如果你有一个全新的模型

需要三步(llama.cpp 官方文档 docs/development/HOWTO-add-model.md 对此有说明):

假设你有一个叫 MyModel 的新架构,HuggingFace 类名是 MyModelForCausalLM,结构和 LLaMA 基本一样。需要改三个地方:

第 1 步:Python 端注册(convert_hf_to_gguf.py,约 20 行)

@ModelBase.register("MyModelForCausalLM")  # ← config.json 的 architectures 字段classMyModelModel(TextModel):    model_arch = MODEL_ARCH.MYMODEL    # 告诉转换器"我的张量叫什么名字":GGUF 命名 → HuggingFace 命名    _model_tensors = {        MODEL_TENSOR.TOKEN_EMBD:  "model.embed_tokens",        MODEL_TENSOR.OUTPUT:      "lm_head",        MODEL_TENSOR.ATTN_Q:      "model.layers.{bid}.self_attn.q_proj",        MODEL_TENSOR.ATTN_K:      "model.layers.{bid}.self_attn.k_proj",        # ... 其他层映射    }

转换时脚本遍历 safetensors 文件,按这个映射把 model.layers.0.self_attn.q_proj.weight 重命名为 GGUF 标准名 blk.0.attn_q.weight,同时把 FP16/BF16 数据原样拷贝。

第 2 步:注册张量布局(gguf-py/gguf/constants.py,约 30 行)

classMODEL_ARCH(IntEnum):    MYMODEL = auto()  # ← 新增# 告诉 GGUF 这个模型有哪些类型的张量,按什么顺序MODEL_TENSORS[MODEL_ARCH.MYMODEL] = [    MODEL_TENSOR.TOKEN_EMBD, MODEL_TENSOR.OUTPUT_NORM, MODEL_TENSOR.OUTPUT,    MODEL_TENSOR.ATTN_NORM, MODEL_TENSOR.ATTN_Q, MODEL_TENSOR.ATTN_K,    MODEL_TENSOR.ATTN_V, MODEL_TENSOR.ATTN_OUT,    MODEL_TENSOR.FFN_NORM, MODEL_TENSOR.FFN_GATE, MODEL_TENSOR.FFN_UP,    MODEL_TENSOR.FFN_DOWN,]

第 3 步:写 C++ Graph Builder(src/models/mymodel.cpp

如果架构和 LLaMA 基本一样,直接参考 src/models/llama.cpp 模板——embedding → N层 Transformer → lm_head。每层:attn_norm → QKV → RoPE → Flash Attn → output → 残差 → ffn_norm → gate/silu × up → down → 残差。约 100~200 行 C++。

做完后还需要在 llama-arch.cpp 注册、llama-model.cpp 关联 llm_type、src/models/models.h 声明。这些都是模板化操作——复制已有架构的注册代码,改名字即可。

第 1+2 步约 50 行 Python(做完就能转 GGUF),第 3 步取决于架构复杂度。llama.cpp 官方文档 docs/development/HOWTO-add-model.md 有完整指南。


第六章:一个完整的加载流程——从命令行到你第一个 token

把上面所有内容串起来。当你在终端输入:

llama-cli -m minimind-f16.gguf -p "你好"

发生的完整流程:

llama_model_load_from_file("minimind-f16.gguf")  │  ├─► gguf_init_from_file("minimind-f16.gguf")  │     → 读 Header: version=3, n_tensors=90, n_kv=33  │     → 读 33 个 KV 对 → gguf_context.kv  │     → 读 90 个 Tensor Info → gguf_context.info  │     → mmap 数据区 → gguf_context.data = 0x7f...  │  ├─► llama_model_loader 构造  │     → 读 "general.architecture" = "qwen3"  │     → 读 超参数: n_layer=8, n_embd=768, n_head=8, ...  │     → 读 tokenizer: 6400 个 token, BPE merges  │  ├─► 确定架构 → LLM_ARCH_QWEN3 → llm_build_qwen3  │  ├─► 在 model_ctx(Arena, no_alloc=true)创建 90 个 ggml_tensor  │     token_embd, output_norm, output  │     blk.0.attn_q, blk.0.attn_k, blk.0.attn_v, blk.0.attn_output  │     blk.0.attn_q_norm, blk.0.attn_k_norm  │     blk.0.ffn_gate, blk.0.ffn_down, blk.0.ffn_up  │     ... (8层)  │     全部 data=NULL  │  ├─► ggml_backend_alloc_ctx_tensors_from_buft(model_ctx, cpu_buft)  │     → 计算总大小 = 122 MB  │     → mmap 映射: 每个 tensor->data 直接指向 GGUF 文件内对应偏移  │     → 或 malloc: 分配 122MB, memcpy 从文件读取  │  └─► 返回 llama_model *         model->layers[0].wq->data → 0x7f... (GGUF 文件内偏移)         model->layers[0].wq->ne   = [768768]         model->hparams.n_layer    = 8         model->vocab.n_tokens     = 6400llama_init_from_model(model, params)  → 创建 llama_context, KV Cache, sampler, ...llama_decode(ctx, batch)  → 第一篇第九章讲的完整推理流程

每个阶段都干净利索,没有多余步骤。这就是 GGUF + mmap + GGML 三者组合的威力——从磁盘文件到跑出第一个 token,中间没有一次完整的数据拷贝。

[图5:完整加载流程] — 五阶段纵向流程图,右侧标注每阶段耗时。mmap 路径总耗时 < 1ms,malloc 路径数分钟。


第七章:核心源码索引

概念
文件:行号
GGUF 二进制格式规范
ggml/include/gguf.h:1-31
gguf_type 枚举(12 种 KV 类型)
ggml/include/gguf.h:53-68
gguf_context 结构体
ggml/src/gguf.cpp:217-228
gguf_init_from_file(读取 GGUF)
ggml/src/gguf.cpp
llama_model_loader 构造
src/llama-model-loader.cpp
从 KV 元数据提取超参数
src/llama-model.cpp
 (llama_model 构造)
创建张量壳子(create_tensor lambda)
src/llama-model.cpp:3074
分配 backend buffer(mmap 分支)
src/llama-model.cpp:8158-8178
架构自动检测匹配表
src/llama-arch.cpp
mmap 文件映射工具
src/llama-mmap.cpp
HuggingFace → GGUF 转换
convert_hf_to_gguf.py
 (13423 行)
新模型适配官方指南
docs/development/HOWTO-add-model.md

第八章:下一篇预告

GGUF 格式和模型加载讲完了。下一篇系列第三篇:130+ 种架构的支持机制——llama.cpp 如何用一套 Builder 模式支持 LLaMA、Qwen、Mistral、Gemma、DeepSeek 等上百种架构?llama-model.cpp 这个 9666 行的最大源文件是如何组织起来的?每种架构的 build() 函数长什么样?

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-05-10 19:06:29 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/602218.html
  2. 运行时间 : 0.349348s [ 吞吐率:2.86req/s ] 内存消耗:4,904.01kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=f0a0f203c2362eb3d14dafde1faf5d91
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000881s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001901s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.006330s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000655s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001727s ]
  6. SELECT * FROM `set` [ RunTime:0.022422s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.002155s ]
  8. SELECT * FROM `article` WHERE `id` = 602218 LIMIT 1 [ RunTime:0.021562s ]
  9. UPDATE `article` SET `lasttime` = 1778411189 WHERE `id` = 602218 [ RunTime:0.015766s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.007412s ]
  11. SELECT * FROM `article` WHERE `id` < 602218 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.022128s ]
  12. SELECT * FROM `article` WHERE `id` > 602218 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.013605s ]
  13. SELECT * FROM `article` WHERE `id` < 602218 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.005260s ]
  14. SELECT * FROM `article` WHERE `id` < 602218 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.087963s ]
  15. SELECT * FROM `article` WHERE `id` < 602218 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.020328s ]
0.353358s