从源码开始:colibri纯C引擎的编译、权重转换与.bin直映射实战
【免费下载链接】colibriRun frontier MoE models on hardware you already own — pure C, zero deps, experts streamed from disk. Tiny engine, immense model. 🐦项目地址: https://gitcode.com/GitHub_Trending/colibri3/colibri
当社区热议“25GB内存跑744B参数大模型”“纯C零依赖推理引擎”时,多数讨论止步于“能跑”这个结果。但对一名工程师而言,真正有价值的问题是另一串:引擎是怎么编译出来的、372GB的权重是如何被重新组织成引擎可以按字节直读的容器、所谓“直映射”与“嵌入式C runtime”到底对应源码里的哪些文件与哪些函数。
colibri(colibrì)正是这样一个适合“从源码开始”学习的项目:它的引擎是一个单文件 C 程序,构建系统能在四种平台工具链间自动切换;它的权重容器没有格式序号,全靠字节算术推断;它的推理路径把“加载权重”做成了内存管理问题,而不是显存容量问题。本文将以仓库真实源码为据,走完编译、转换、直映射、运行时验证这条完整链路,把宣传性描述还原为可复现的工程事实。
零依赖编译:从 clone 到出二进制
项目的仓库结构把“引擎”与“工具链”物理隔离:引擎本体是纯 C,Python 只出现在一次性转换器(c/tools/convert_glm53.py)与可选 API 网关(c/openai_server.py)中,运行时没有任何 Python 依赖。九个模型家族各有一个引擎文件——c/colibri.c(GLM-5.2,12738 行)、c/inkling.c、c/kimi_k3.c、c/glm53.c、c/qwen36.c、c/olmoe.c、c/deepseek_v4.c——共享同一批头文件:safetensors 读取器(c/st.h)、量化解码(c/quant.h)、分词器(c/tok.h)、专家缓存(c/expert_store.h)。这个“一个模型一个 .c、共享单头文件”的布局(见 README.md 的 Repo layout 小节)意味着修复一个机制即可惠及所有引擎。
编译入口是根目录 Makefile,它只做一件事:把目标委派给 c/Makefile。后者真正的精巧之处在于平台检测:它不用uname,而是用$(CC) -dumpmachine读编译器自身的 target triple,因此无论是原生 Windows shell(没有uname)、MSYS2 还是交叉编译,判断都跟随工具链而非宿主:
TRIPLET := $(shell $(DETECT_CC) -dumpmachine 2>/dev/null) MINGW := $(findstring mingw,$(TRIPLET)) DARWIN := $(findstring darwin,$(TRIPLET)) LINUX := $(findstring linux,$(TRIPLET))随后按平台分化出四条构建分支:Linux x86-64 默认ARCH=native(-march=native解锁 AVX2/AVX-VNNI 点积核),Windows 默认ARCH=x86-64-v3以得到可移植的 AVX2 二进制,Apple Silicon 用 clang + Homebrew/MacPorts libomp(找不到就退化为单线程并明确警告),aarch64 Linux 甚至会对__ARM_FEATURE_MATMUL_INT8做编译探测——如果编译器无法为当前核生成 i8mm 指令,就自动回退到更保守的-march=armv8-a(对应 c/Makefile 中的 #631 修复)。CUDA/HIP/Metal/Vulkan 全部是CUDA=1、METAL=1、VK=1之类的显式开关,默认不编译,CPU 构建零第三方依赖。
实际动手只需要两步。./setup.sh(c/setup.sh)会检查 make、gcc/clang、OpenMP,然后执行make -s colibri ARCH="${ARCH:-native}"并跑自检:
cd colibri/c ./setup.sh # 检查 gcc/OpenMP,构建引擎,运行 tiny 自检构建系统还有一个值得注意的细节:.build-config配置戳(c/Makefile 中 #306 的修复)。它记录$(CC)|$(CFLAGS)|$(LDFLAGS)|CUDA=...|ARCH=...,仅在配置变化时重写并触发重链,避免了“先make colibri.exe再make colibri.exe CUDA_DLL=1却静默得到旧二进制”这类看似成功实则错误的构建。自检环节更有意思——它不是简单的“能启动”,而是对照一个随机初始化的小模型做逐位置 token 级验证:
SNAP=./glm_tiny TF=1 ./colibri 64 16 16 # engine self-test: 30-32/32 positions (FP near-ties 与工具链相关,属正常)这个 tiny oracle 由 c/tools/make_glm_oracle.py 生成:先在 FP8 上做一次权重 round-trip,再计算ref_glm.json的 greedy 参考输出,确保转换器读到的模型与引擎解码的模型是同一个,从而把“编译器改变浮点收缩顺序导致 token 漂移”这类回归挡在 CI 之外。这正是 README 里“no SLA on speed, hard guarantee on semantics”的落地方式:速度没有承诺,语义正确性是硬约束。
权重转换:从 756GB FP8 到按字节直读的 int4 容器
拿到引擎二进制之后,真正的重头戏是权重。社区情报反复强调的“.bin 权重直映射”值得拆开讲:它不是一个单一格式,而是一整套“布局即 ABI”的设计——权重的字节布局就是 C 引擎可以直接消费的结构,省去了运行时解码索引层。
转换入口是./coli convert --model <dir>,底层脚本是 c/tools/convert_glm53.py 与 c/tools/convert_fp8_to_int4.py。两条硬性工程约束决定了它的写法。
第一,磁盘安全。脚本逐 shard 下载、逐 shard 转换、转完即删源文件,全程不需要 756GB 同时落地;--min-free-gb在每 shard 前检查剩余空间,空间不足就[WAIT]轮询而不是失败。第二,可续传。.converted.json记录已完成 shard,中断后重跑自动跳过,这正是 WSL2 部署实践中“转换耗时数小时但可随时中断”说法的源码出处。
真正决定“直映射”品质的是量化器 quant_int4_grouped:
def quant_int4_grouped(w, gs=64): qmax = 7 ngroups = (I + gs - 1) // gs ... q = np.clip(np.rint(wr / s), -8, qmax).astype(np.int32) v0 = (q[:, 0::2] + 8).astype(np.uint8) # 偶数列进低 nibble out[:, :v0.shape[1]] = v0 v1 = (q[:, 1::2] + 8).astype(np.uint8) out[:, :v1.shape[1]] |= (v1 << 4) # 奇数列进高 nibble注意文件头注释的承诺:“Copia bit-identica … stesso np.rint, stesso packing dei nibble”——转换器与引擎共享完全相同的舍入(np.rint对应 C 侧lrintf)与 nibble 打包规则,因此转换产物无需任何对齐修正即可被 c/quant.h 的matmul_i4_grouped直接消费。转换器还按张量名分类路由:.mlp.experts.*走 int4 量化(每 64 元素一个 f32 scale),embed_tokens、各 proj 权重保持 BF16,dt_bias、norm.weight等保留 F32,因为后者只占约 3% 参数量,不值得为它们付出精度代价。
引擎读取这些容器的方式,是“直映射”的第二层含义:没有格式序号,格式从字节数推断。权威读取器qt_resolve_fmt(c/colibri.c)只凭“权重字节数 + scale 字节数与[O,I]形状的关系”即可判定张量属于 fmt=0…8 中的哪一种。c/../docs/FORMATS.md 维护了完整的格式注册表:0 f32、1 int8-row、2 int4-row、3 int2-row、4 int4-grouped、5 int3-g64、6 E8/IQ3、7 MXFP4、8 fp8-e4m3-b128。容器在磁盘上仍是 safetensors,但读取路径是 c/st.h 的pread直接按偏移读字节——刻意不用 mmap,配合posix_fadvise(DONTNEED)防止页面常驻进程,这是修复“mmap 导致 RSS 爆炸”历史 bug 的关键决策;O_DIRECT双胞胎 fd 则让大块流式读绕过 page cache,在 ext4-in-VHDX 上实测从 0.8 GB/s 提升到 2.3+ GB/s。
路由专家的“直映射”到达极致体现在 c/expert_ffn.h 的 planar 布局上。一个 64 元素的块是 32 字节,字节 k 的低 nibble 存元素 k、高 nibble 存元素 k+32,全部以v+8无符号形式存储。这样 SIMD 解包时and 0x0F就能按序得到 0..31,srli 4得到 32..63——永远不需要付出 unpack 指令,而无符号 nibble 又恰好原生喂给vpdpbusd(dot(u,x) = dot(u,x) - 8*sum(x))。这就是为什么 Qwen3.6 全驻留实测中,MoE 权重读取与计算能从“int8 解包展开”的 34ms/token 降到“int4 原始字节”的 9ms/token(见 c/expert_ffn.h 头注释)。
至于字面意义上的.bin文件,它们承担的是缓存状态直映射:学习到的热专家固定清单写入hot_pinned.bin(c/olmoe.c)、Kimi K3 的循环状态检查点写为k3_ckpt_*.bin(c/kimi_k3.c)、DeepSeek V4 的磁盘前缀检查点是ckpt_<fingerprint>_<n>.bin(c/deepseek_v4.c),而每 turn 更新的路由热度落在.coli_usage、跨重启的 KV 状态落在.coli_kv。这些侧车文件不是模型权重,却决定了下一次推理的延迟——热专家被固定后,解码可以从“每次从磁盘拉取”变为“命中常驻集”。
嵌入式 C runtime:从单机流式到端到端低延迟验证
权重容器设计好了,剩下的问题是如何在“25GB 内存 + 一块 NVMe”上把 744B 参数跑成低延迟服务。答案藏在两处源码里。
第一处是流式推理的核心路径。每个 token 的每一层都走同一套五步:route → union → place → overlap → learn。路由决定激活哪些专家;被激活专家做 batch-union(同一 batch 内每个专家只读一次);放置决定专家从哪个 tier 取;重叠让磁盘读取与计算并行;学习把高频路由写回.coli_usage。支撑这五步的机制分别在 c/st.h(单个 expert 的三张矩阵相邻存放、一次pread读完)、c/expert_store.h(有界异步 I/O 池PIPE=1)、以及 router-lookahead 线程(PILOT=1,路由可预测性实测 71.6%)。多层级的放置关系用一张图就能说清:稠密部分(attention、共享专家、embedding,约 17B 参数)以 int4 常驻 RAM(约 9.9GB),19456 个路由专家(75 MoE 层 × 256,int4 下每个约 19MB)躺在磁盘上按需流入,LRU 缓存 + 学习到的固定热集 + 可选的 VRAM tier 构成三级安置。
第二处是嵌入式 ABI。社区情报提到的“嵌入式 C runtime”对应 c/segment_runtime.h 与 c/edge_runtime.h 两个引擎中立边界:make -C c segment-edge-library产出c/build/segment/libcolibri_segment_edge.a,外部消费者链接该静态库后,就能驱动一个“只跑中间层段”的引擎——tokenizer、embedding、final head 在边界进程,中间层在 Segment 引擎里,两者通过coli_segment_run传递边界激活与状态快照。七个模型家族都提供了注册即用的适配器(c/segment_adapters.h、c/edge_adapters.h),而普通 CLI 默认不链接这些适配器,存量推理路径完全不受影响——这是“嵌入式”而非“侵入式”的关键。
端到端低延迟的验证体系同样扎实。正确性维度:tiny 夹具把引擎输出与 transformers oracle 逐 token 对比(c/tests/glm53_tiny_harness.py),teacher-forcing 与 greedy 双通道都精确到 token;MLA 注意力把每个 token 的 KV 状态从 32768 浮点压到 576(57× 压缩),并持久化到.coli_kv,跨重启的对话无需重新 prefill 即可“温启动”,字节级等同未中断会话。延迟维度:ladder 图给出了同一容器在不同硬件上的诚实刻度——6×RTX 5090 全驻留 5.8–6.8 tok/s、128GB CPU 桌面约 1.8 tok/s、25GB 开发机冷启动 0.05–0.1 tok/s。项目明确把最后这个数字当作“诚实的下限基线”而非成绩,因为它的设计哲学是:放置只改变速度,绝不悄悄改变模型语义。
在端到端路径上还有一层低延迟优化与“生成”解耦:Brio 模式不生成 token,而是直接读取候选答案的概率并输出熵(completion_tokens为 0),文档阅读一次、答案必须在允许集合内,实测在同一 CPU 机器上比生成式回答快 2.4×(四字段 schema)到 5.7×(四问一文档),详见 c/../docs/brio.md。当“低延迟”的诉求是快速判定而非生成文本时,这是引擎给出的另一个答案。
结语
把社区情报与仓库源码对照后会发现,colibri 最稀缺的不是“744B 能跑”这个结果,而是它把每个性能论断都做成了可测量的工程承诺:编译器切换用 target triple 而不是宿主名,量化器与解码器共享同一套舍入数学,格式身份靠字节算术而非协调出来的序号,正确性靠 tiny oracle 的逐 token 对比而非抽样印象。对于想研究 MoE 推理系统的人,这条“编译 → 转换 → 直映射 → 端到端验证”的链路本身,就是一份完整且可复现的教材。
【免费下载链接】colibriRun frontier MoE models on hardware you already own — pure C, zero deps, experts streamed from disk. Tiny engine, immense model. 🐦项目地址: https://gitcode.com/GitHub_Trending/colibri3/colibri
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考