代码结构与构建
1. 单文件设计哲学
colibrì 的整个运行时引擎就是一个 C 文件 c/glm.c(约 2547 行)加几个 header-only 头文件。所有第三方库(safetensors 读取、JSON 解析、tokenizer)都是头文件形式内联进来的,编译产物是一个零依赖的可执行文件。c/Makefile 的注释明确写道:默认构建”remains pure C and keeps the original zero-dependency runtime”。
这带来两个直接后果:
- 极致可移植:一条
gcc -O3 -march=native -fopenmp glm.c -lm就能编译整个引擎,没有 CMake、没有 vendored 库、没有pip install。 - env-var 驱动:因为没有复杂的配置系统,几乎所有运行时行为都由环境变量控制,集中在 main 里解析(
SNAP/PROMPT/SERVE/DRAFT/PIN等)。
代码注释混用意大利语和英语——作者的工作语言是意大利语,较新的补充带有意/英双语注释。
2. 顶层目录
colibri/
├── Makefile 根构建入口(4 行,全部转发给 c/)
├── README.md 主文档(26.5 KB,含 benchmark 与设计哲学)
├── ref.json OLMoE oracle:prompt + prompt_ids/full_ids
├── assets/colibri.svg logo
├── .github/ issue/PR 模板
├── c/ 引擎本体(全部 C / CUDA / Python)
└── web/ 浏览器 UI(React + Vite,社区维护)
c/ 目录里,运行时路径刻意保持扁平、可读:
| 文件 | 行数 | 角色 |
|---|---|---|
| glm.c | 2547 | 单文件 GLM-5.2 引擎(本分析的主角) |
| olmoe.c | 390 | Stage-A 参考引擎(OLMoE,验证流式核心的前身) |
| iobench.c | 58 | 磁盘微基准(19 MB 随机读,模拟专家读法) |
| st.h | 233 | safetensors 读取器(pread + fadvise,非 mmap) |
| json.h | 149 | 极简 JSON 解析器 |
| tok.h | 278 | GLM-5.2 byte-level BPE tokenizer |
| tok_unicode.h | 162 | Unicode 属性表(由脚本生成) |
| tier.h | 31 | 专家分层/固定的热度换入换出 |
| compat.h | 50 | 跨平台垫片(Linux 上全是 no-op) |
| backend_cuda.h / .cu | 49 / 230 | 可选 CUDA 层 |
| coli | — | 面向用户的 Python CLI 调度器(chat/serve/plan/convert…) |
| openai_server.py | 605 | OpenAI 兼容 HTTP 网关 |
| resource_plan.py | 216 | RAM/磁盘/VRAM 资源规划器 |
| setup.sh / scripts/ / tools/ / tests/ | — | 安装、转换、基准、测试(均非运行时依赖) |
Python 与 shell 工具刻意分组隔离,永远不是引擎的运行时依赖——它们只在离线转换、基准、CLI 包装时用到。
3. 构建系统
3.1 根 Makefile — 纯转发
根 Makefile 只有 4 行,每个目标都转发进 c/:
.PHONY: all glm portable test check cuda-test clean
all glm portable test check cuda-test clean:
$(MAKE) -C c $@
3.2 c/Makefile — 平台分流
c/Makefile(99 行)按 uname -s(第 1 行)分流:
macOS / Apple Silicon(第 3–17 行):用 clang,探测 Homebrew 的 libomp 来启用 OpenMP;注释说 arm64 上不加 -march——NEON 是基线,__ARM_NEON 内核自动生效。
Linux x86-64(第 20–29 行):用 gcc,ARCH ?= native,可选 x86-64-v3(可移植,需要 AVX2)或 x86-64(最大兼容,标量回退)。核心编译标志:
CFLAGS = -O3 -march=$(ARCH) -fopenmp -Wall -Wextra ...
LDFLAGS = -lm -fopenmp
CUDA opt-in(第 31–48 行):默认 CUDA ?= 0(关闭)。CUDA=1 时加 -DCOLI_CUDA,用 nvcc 编译 backend_cuda.cu,链接 -lcudart -lstdc++。在 macOS 上 CUDA=1 会直接 $(error ...)。
关键点:SIMD ISA 是编译期选择,不是运行期分发。具体走 AVX512-VNNI / AVX2 / NEON / scalar 哪条路,由 glm.c 里的 #ifdef 宏在编译时决定——所以要发挥某台机器的性能,需要用 -march=native 为它重新编译。
常用目标:glm(主引擎)、portable(= make glm ARCH=x86-64-v3)、cuda-test、test(C + Python 测试)、check(clean + portable + test,本地验证)。
3.3 setup.sh — 一条命令上手
c/setup.sh(48 行):检查 gcc/clang + OpenMP,make -s glm ARCH=native 构建,跑一个 tiny-oracle 自检(期望输出 32/32),打印 RAM 和下一步命令。注释警告:模型要放在快速 NVMe/ext4 上,绝不能放 /mnt/c 或网络挂载。
4. glm.c 的分区结构
虽然是一个文件,glm.c 逻辑上分区非常清晰。下表是按行号的地图(每个区段对应后续的一篇或几篇文档):
| 行范围 | 区段 | 对应文档 |
|---|---|---|
| 1–18 | 文件头 / 架构摘要 | 01 |
| 19–51 | includes、平台 #if、AVX2/NEON hsum256 | 02 |
| 53–144 | 核心结构体:QT、Cfg、ESlot、Layer、Model | 03/04 |
| 145–199 | CUDA 全局与助手、now_s/rss_gb/falloc | 09 |
| 200–296 | F32/dequant-on-use 矩阵乘:matmul/_q/_i4/_i2 | 03 |
| 298–455 | IDOT 整数点积内核:dot_i8i8/dot_i4i8/matmul_*_idot | 03 |
| 456–491 | matmul_qt 分派器(CUDA / IDOT / 标量回退) | 03 |
| 492–582 | 量化器 quantize_rows/pack_int4/pack_int2、env 标志 | 03 |
| 583–610 | 数学:rmsnorm/layernorm/softmax/rope_interleave | 03/06 |
| 611–705 | 配置与张量加载:load_cfg/qt_from_disk/ld | 04 |
| 706–846 | model_init(大加载器) | 04 |
| 847–955 | embedding 与专家 I/O:embed_row/expert_load/expert_prefetch | 04/07 |
| 956–1141 | 注意力:attention(MLA + DSA indexer + 吸收) | 06 |
| 1142–1275 | moe(5 阶段路由)、dense_mlp | 07 |
| 1276–1347 | 投机预取:la_predict(LOOKA)、pilot_prefetch(PILOT) | 07/08 |
| 1348–1425 | 前向:layer_forward/layers_forward/step/step_all | 05 |
| 1427–1507 | 投机解码起草:ngram_draft/mtp_draft/mtp_absorb | 08 |
| 1508–1628 | 采样:dist_build/pick_tok/spec_decode | 05/08 |
| 1629–1783 | 运行模式:forward_all/run_score/generate/run_text | 05 |
| 1784–1937 | SERVE + 热 repin + KV 落盘 | 06/07/10 |
| 1938–2091 | 多 slot 服务:ServeCtx/run_serve | 10 |
| 2091–2382 | 资源预算与学习缓存:cap_for_ram/usage_load/pin_load | 04/07 |
| 2384–2547 | main:env-var 解析、模式分派 | 05 |
5. 头文件
5.1 st.h — safetensors 读取器
st.h 是 colibrì 磁盘流式的基石。头注释(1–6 行)点明了一个关键设计:用 pread + posix_fadvise(DONTNEED) 而不是 mmap,专门为了修复 “mmap-rss-bug”——让常驻内存保持稠密可控。
一条设计注释(36–38 行)解释了为什么要建哈希表:GLM 有约 12 万个张量(256 专家 × 78 层 × 3 × 2),线性扫描每 token 要花几十秒。O_DIRECT 双 fd 的动机在另一处注释(93–94 行):VHDX 里的 ext4 缓冲读只能到 ~0.8 GB/s,O_DIRECT 能到 2.3+ GB/s。
5.2 json.h — 极简 JSON 解析器
json.h(149 行)是递归下降解析器,用于读 safetensors 头和 ref.json。支持 \uXXXX 和代理对,但不追求完整 spec。核心:jval 结构、json_parse、json_get。
5.3 tok.h / tok_unicode.h — tokenizer
tok.h(278 行)是忠实实现 GLM-5.2 的 byte-level BPE(cl100k/tiktoken 风格,ignore_merges=true,ByteLevel 预分词,320k merges)。关键函数:tok_load(解析 tokenizer.json)、bpe_piece(BPE 合并循环)、pretok_chunk(cl100k 正则,7 个分支)、tok_encode、tok_decode。
tok_unicode.h 是由 tools/gen_unicode.py 生成的 Unicode 属性表(字母/数字/空白范围 + 二分查找),供预分词正则使用。
5.4 tier.h — 专家分层换入换出
tier.h(仅 31 行)实现热度驱动的固定专家换入换出:
tier_pick_swap(8 行):从路由热度里挑一个热存储 slot 来替换——找最冷的已固定专家和最热的未常驻专家,只有当fh > fc + fc/4 + 4(25% + 固定 4 的迟滞)时才换,防止 ping-pong。tier_decay(27 行):每次把所有热度计数右移一位(heat[e] >>= 1)。
5.5 compat.h — 跨平台垫片
compat.h(50 行)在 Linux 上是完全的 no-op——所有平台差异都收拢在这里,让 .c 保持干净。逻辑全在 #ifdef __APPLE__ 里:把 posix_fadvise(WILLNEED) 映射到 F_RDADVISE、DONTNEED 映射到 no-op,把 O_DIRECT 映射到 F_NOCACHE。
6. 其他 C 文件
- olmoe.c(390 行)是 Stage-A 参考引擎:一个更简单的纯 C OLMoE 推理引擎,头注释说它的目标是复现
ref.json的精确 token id,用来在扩展到 GLM-5.2 之前验证”磁盘流式 + 注意力”的核心。它是glm.c的前身——同样的专家流式思想,但没有 CUDA、KV 落盘、HTTP 服务。详见 10-服务与工具链。 - iobench.c(58 行)是磁盘微基准:用 N 个线程并行
pread专家大小(~19 MB)的随机块,测量随机读带宽——正是引擎读专家的方式。README 里教用户先跑它来预测自己机器的 token 速度。