适用边界与限制
前面十篇讲的是 colibrì “能做什么、怎么做到”。这一篇讲它不能做什么——模型格式、量化方案、精度代价、硬件平台的边界。理解这些边界,才能判断它适不适合你的场景。
一句话定性:colibrì 不是通用推理引擎,而是一个”让 GLM-5.2 这一个模型在消费级 CPU 机器上跑起来”的专用工程。它和 llama.cpp 是两条不同的路线——llama.cpp 求”多模型 + 成熟量化 + 多后端”,colibrì 求”单模型 + 磁盘流式 + 极简 C”。
1. 模型格式:只吃 safetensors,不支持 GGUF
输入容器只认 safetensors。加载器 st.h 索引目录下所有 *.safetensors 分片。运行时 dtype 只处理 BF16 / F16 / F32(st_dtype_code,st.h:49-55),遇到其他 dtype 直接 exit;FP8(E4M3)由离线转换器处理。
- 不支持 GGUF / ggml —— 全仓库零匹配。
- 要运行,必须用它的离线转换器 convert_fp8_to_int4.py 把官方 FP8 权重转成它的容器,或直接下载 HF 上转好的
GLM-5.2-colibri-int4。
预量化容器的布局是:每个权重张量 name(U8 打包字节)+ 旁挂一个 name.qs(F32 per-row 缩放),由 qt_from_disk 读取。这是 colibrì 自定义的格式,与任何现有生态都不通用。
2. 量化方案:朴素 per-row,不是 Q4_K_M
colibrì 的量化是最朴素的 per-row 对称 absmax,和 llama.cpp 的 k-quant 完全不是一回事:
| colibrì | llama.cpp (GGUF) | |
|---|---|---|
| 容器 | safetensors + 旁挂 .qs | 单文件 GGUF |
| int4 方案 | per-row:每行一个 f32 scale(pack_int4) | Q4_K:super-block + 双层 scale/min |
| 加权 | 无 | imatrix(重要性矩阵)可选 |
| 打包 | int4=2/字节偏置8、int8、int2=4/字节 | Q4_K_M / Q5_K / Q6_K 混合精度 |
- 不支持 Q4_K_M / Q5_K / Q6_K / k-quant / imatrix —— 全仓库零匹配。
- colibrì 的 int4 没有 Q4_K_M 的分组 min/scale,也没有重要性矩阵加权。同等 bit 下,精度大概率不如 llama.cpp 的 Q4_K_M——这是为极简和 dequant-on-use 主动做的取舍。
- 详见 03-量化与矩阵乘内核。
3. 架构写死:基本是 GLM-5.2 专用引擎
这是最容易被忽略、也最关键的限制。
- 架构硬编码。glm.c 实现的是 GLM-5.2 的
glm_moe_dsa。load_cfg 硬断言n_group==1,否则直接 exit(654 行)。MLA 维度、sigmoid+noaux_tc 路由、DSA lightning indexer、MTP 头都是照 GLM-5.2 写的——换一个结构不同的 MoE(Mixtral、Qwen-MoE 等)不能直接跑。 - olmoe.c 是一个独立文件,专门给 OLMoE,是
glm.c的验证前身,二者不通用。 - 只做文本。服务端显式拒绝 tools、图像/音频、自定义 stop、logprobs、penalties——返回明确错误而非静默忽略。
- 服务是单序列。引擎一次只执行一个生成,并发 HTTP 请求走有界 FIFO 队列(429/503);
--kv-slots只是显式 KV 归属,不是连续 batching。 - 模型必须在本地 NVMe/ext4 上,不能放网络 / 9p /
/mnt/c挂载——热路径是海量随机pread,setup.sh 明确警告。
4. 性能与精度代价
4.1 性能:decode 是磁盘受限的
成本模型:「每 token ≈ 11 GB 专家读 ÷ 磁盘随机读带宽」。实测跨度极大(见 README benchmark 表):
| 机器 | 磁盘 | 实测 |
|---|---|---|
| WSL2 VHDX | ~1 GB/s | 0.05–0.1 tok/s(冷) |
| Apple M5 Max | 14 GB/s SSD | ~1 tok/s(纯 CPU) |
| Ryzen AI Max+(学到热专家 pin) | 3.3 GB/s | 0.40 tok/s(最快非 Apple) |
过了 ~5 GB/s,盘不再是瓶颈,CPU 矩阵乘接棒(9950X 换 PCIe5 盘后 profile 从 66% disk 翻转到 57% matmul)。缓存暖 + MTP 投机 + 热专家 pin 能进一步压低有效延迟(见 07、08)。
4.2 精度:未系统评测,量化偏朴素
- 质量从未被系统测过。README 作者原话:”We have never measured how much the int4 quantization costs in accuracy”——在 ~1 GB/s 的盘上跑一遍 MMLU 要大半天。harness 已就绪(
coli bench),但缺快机器实测。 - int4 是朴素 per-row,同等 bit 下精度大概率不如 Q4_K_M。
- 引擎的补偿:采样默认收紧到 0.7 / 0.90(g_temp auto 0.7、g_nuc 0.90),因为 int4 分布尾部是量化噪声。
- 前向本身验证过 token 精确(对 transformers oracle:TF 32/32、贪心 20/20)。MLA 权重吸收、DSA、KV 持久化都验证过逐 token 一致——误差来自量化本身,不是引擎实现。
- IDOT 整数点积额外引入约 0.3% RMS 误差(
IDOT=0可关)。 - MTP 头必须 int8:int4 下 draft 接受率崩到 0–4%,投机失效(见 08)。
结论:能不能对不知道(没测),但能对的地方是真能对(前向精确)。
5. 硬件平台支持
| 平台 | 支持 | 说明 |
|---|---|---|
| x86-64 CPU | ✅ | AVX2 基线,AVX512-VNNI 编译期自动启用 |
| ARM CPU / Apple Silicon | ✅ | NEON 内核 + macOS F_NOCACHE(compat.h),M5 Max ~1 tok/s——纯 CPU |
| NVIDIA CUDA | ✅ 实验/可选 | 仅常驻张量 + 热专家,流式专家仍走 CPU(见 09) |
| AMD ROCm / HIP | ❌ | 全仓库零匹配 |
| Apple MPS / Metal | ❌ | Apple Silicon 只用 CPU 的 NEON,不碰 GPU |
| Vulkan / SYCL / OpenCL / Intel GPU | ❌ | 零匹配 |
其他硬件约束:
- CUDA 只在 Linux。macOS 上
CUDA=1直接$(error CUDA=1 is supported only on Linux)(Makefile:42-43)。 - 即使有 CUDA,流式专家也故意不上 GPU——每次从 NVMe 拷到显存只会把磁盘瓶颈换成 PCIe 瓶颈;只有常驻张量 / pin 热专家才上 GPU(matmul_qt 的 CUDA 分派条件)。
- CUDA kernel 是 correctness-first 自写的,不是 cuBLAS/Tensor Core;多卡没有 P2P/NCCL,官方明确”暂不做端到端加速声明”。
- 需要 ≥16 GB RAM(24 GB 以下缓存被自动限到每层 2 槽,decode 永远冷)、Linux 或 WSL2、gcc + OpenMP、AVX2。
6. 一图看清适用边界
✅ 适合 ❌ 不适合 / 不支持
────────────────────────────── ──────────────────────────────
跑 GLM-5.2 这一个模型 跑 Mixtral / Qwen-MoE / 任意架构
消费级 CPU + 大容量本地 NVMe GGUF / Q4_K_M 生态
把磁盘当"显存",接受慢速换可行 追求高吞吐 / 低延迟
纯文本对话 / 单序列服务 多模态 / tools / 连续 batching
零依赖、可 `gcc glm.c` 编译 ROCm / Metal-MPS / Vulkan 加速
研究"极限硬件下跑大模型"的工程 生产级、经过精度评测的部署
7. 小结
| 维度 | 边界 | 出处 |
|---|---|---|
| 模型格式 | 只吃 safetensors,不支持 GGUF | st.h:49 |
| 量化方案 | 朴素 per-row,不支持 Q4_K_M/k-quant | pack_int4:505 |
| 模型架构 | 写死 GLM-5.2 glm_moe_dsa,断言 n_group==1 | load_cfg:654 |
| 精度 | 未系统评测;前向精确,量化偏朴素 | README quality 章节 |
| GPU 加速 | 仅 NVIDIA CUDA(Linux),无 ROCm/Metal/Vulkan | Makefile:42 |
| 服务 | 单序列 + FIFO,非连续 batching;仅文本 | openai_server.py:56 |