第 6 章 · 01 routetrace.h engine-agnostic 遥测设计 本节摘要: 是 Colibrì 路由遥测的归属地,集中了 ROUTETRACE 流和 专家历史,让所有引擎发出同样的字节。它故意从 独立出来——后者绑死了 GLM 的 Model/Cfg/QT/ESlot 结构,kimik3.c 和 olmoe.c 因为类型不同无法 include。路由遥测只需要 layer/expert/gate 三个无类型依赖的量,所以单独成头。 是文本格式 稀疏记录,负层号头( 维度 / 版本+engineid)用旧 reader 的 守卫自然向后兼容;engineid 必须是第三字段(32 位 hash 超 INTMAX,第二字段 会 UB)。
本节摘要:
route_trace.h是 Colibrì 路由遥测的归属地,集中了 ROUTE_TRACE 流和.coli_usage专家历史,让所有引擎发出同样的字节。它故意从telemetry.h独立出来——后者绑死了 GLM 的 Model/Cfg/QT/ESlot 结构,kimi_k3.c 和 olmoe.c 因为类型不同无法 include。路由遥测只需要 layer/expert/gate 三个无类型依赖的量,所以单独成头。.coli_usage是文本格式<layer> <expert> <count>稀疏记录,负层号头(-1维度 /-2版本+engine_id)用旧 reader 的l>=0守卫自然向后兼容;engine_id 必须是第三字段(32 位 hash 超 INT_MAX,第二字段%d会 UB)。
内容来源:原项目源码
c/route_trace.h(注释 L1-50)
⚠️ 注意:本节深潜的是 route_trace.h 的注释而非实现——这 50 行注释的设计信息密度极高,每一个排版决策(负层号、字段顺序、
%uvs%d)背后都有具体约束。读懂这些注释,就读懂了 Colibrì 如何在"多引擎共享遥测格式"和"严格向后兼容"之间取得平衡。
.coli_usage 格式:文本三元组稀疏 + 负层号头记录。l>=0 守卫自然丢弃头记录。%u 避免 INT_MAX UB),以及 legacy/IKU1 旧格式。文件开头第一段注释就回答了"为什么不放进 telemetry.h":
1 /* route_trace.h — engine-agnostic routing telemetry: the ROUTE_TRACE stream and the 2 * .coli_usage expert history, in one place so every engine emits the same bytes. 3 * 4 * Why a separate header and not part of telemetry.h: telemetry.h is bound to GLM's 5 * structures ("include after Model/Cfg/QT/ESlot/shards ... requires qt_bytes(), now_s(), 6 * rss_gb()"), so kimi_k3.c and olmoe.c cannot include it — their Model/Cfg are different 7 * types. Routing telemetry does not need shared types: an engine knows its layer index, 8 * the expert ids it selected and their gates, and that is the whole input.
关键矛盾:Colibrì 有五个模型引擎(colibri.c/GLM、deepseek_v4.c、inkling.c、kimi_k3.c、olmoe.c),它们的 Model、Cfg 类型彼此不同。telemetry.h 里到处引用 GLM 的 Model/Cfg/QT/ESlot/shards,以及 qt_bytes()、now_s()、rss_gb() 这些只在 GLM 上下文才有的函数,所以 kimi_k3.c 和 olmoe.c 想 include 它根本编不过——类型对不上。
但路由遥测不需要任何引擎特定类型。一个引擎只要知道自己当前在第几层(layer)、选了哪些专家(expert ids)、它们的 gate 是多少(gate),就足够了。这三个量是所有 MoE 引擎共有的抽象,与具体模型结构无关。所以把路由遥测拆出来,让所有引擎都能 include,是唯一的合理设计。
注释第 10-18 行强调了一个容易踩的坑——只 include <stdio.h> 看似能编过、Linux 测试也过,但会在 Windows 上重新引入 bug:
10 * Dependencies are the C library plus compat.h, and compat.h is NOT optional here: it 11 * redirects rename() to MoveFileEx(MOVEFILE_REPLACE_EXISTING), because the CRT rename 12 * fails with EEXIST when the destination exists and would therefore silently stop 13 * updating the history after its first write on Windows. That shim exists in compat.h 14 * *because of* this very code path — see the comment above compat_rename(). Including 15 * only <stdio.h> here compiles fine and passes on Linux; it reintroduces that bug. 16 * compat.h itself is self-contained (system headers only, no engine types), which is why 17 * st.h can include it too.
CRT 的 rename() 在目标已存在时返回 EEXIST 失败,导致 Windows 上 .coli_usage 第一次写完后就再也不更新——静默 bug。compat.h 把 rename() 重定向到 MoveFileEx(MOVEFILE_REPLACE_EXISTING) 绕过这个限制。注释明确说:这个 shim 之所以存在于 compat.h,就是因为 route_trace.h 这条代码路径。
compat.h 本身只依赖系统头,不引任何引擎类型,所以 st.h 也能 include 它。这是一个精心设计的依赖分层:route_trace.h → compat.h → 系统头,整条链无引擎类型污染。
第 19-29 行定义历史文件格式:
19 * ---- history format (.coli_usage) ---- 20 * Text, one record per line: "<layer> <expert> <count>", sparse (non-zero only). 21 * Two header records carry the dimensions and the writing engine's identity: 22 * 23 * -1 <n_layers> <n_experts> 24 * -2 <format_version> <engine_id> 25 * 26 * The layer field is NEGATIVE on purpose. Every reader written against the old format is 27 * a `while(fscanf(f,"%d %d %u",&l,&e,&c)==3)` loop guarded by `l>=0`, so it parses these 28 * two records, discards them, and continues into the data — which is what lets a file 29 * written here still load in an engine built before this header existed.
正文格式:每行 <layer> <expert> <count>,文本,稀疏(只记非零)。文本格式的最大好处是可读、可 grep、可 diff,运维友好。
两条头记录用负层号:第 23 行 -1 <n_layers> <n_experts> 记维度;第 24 行 -2 <format_version> <engine_id> 记版本和引擎身份。这里最精妙的设计是负层号:
所有按旧格式写的 reader 都是 while(fscanf(f,"%d %d %u",&l,&e,&c)==3) 加 l>=0 守卫。当它们读到 -1 ... 这一行,l 被解析成 -1,l>=0 守卫失败,记录被丢弃;读到 -2 ... 同样丢弃;读到真正的数据行(l>=0)才采纳。两条头记录被旧 reader 自然丢弃,文件能直接在老引擎里加载——这是无显式版本协商的向后兼容。
第 31-36 行讲两个"很容易踩错"的字段排版约束:
31 * Two constraints that field layout is forced by, and they are easy to get wrong: 32 * - a string cannot go in any slot: a non-numeric field makes fscanf return < 3, which 33 * ends the loop and silently drops every record after it. Hence engine_id is a hash. 34 * - engine_id must sit in the THIRD field, the one old readers parse with "%u". A 32-bit 35 * hash routinely exceeds INT_MAX and the second field is read with "%d", where that 36 * is undefined behaviour. So the version (small) goes second, the hash third.
约束一:任何字段都不能放字符串。一旦出现非数字字段,fscanf 返回值小于 3,while 循环立即终止,该记录之后的所有记录全部静默丢失。所以 engine_id 不能直接存引擎名字符串,只能存 32 位 hash(FNV-1a,见代码 L65-69)。
约束二:engine_id 必须在第三字段。32 位 hash 经常超过 INT_MAX(2^31-1),而旧 reader 把第二字段用 %d 读——%d 读到超过 INT_MAX 的值是未定义行为(UB)。第三字段旧 reader 用 %u 读 unsigned,无 UB。所以版本号(永远是小整数)放第二字段,hash 放第三字段。这是被 fscanf 格式符和整数范围硬逼出来的字段顺序,不能调换。
第 38-44 行说明还读两种更老的格式:
38 * Two older layouts are also read, so histories accumulated before this existed keep 39 * working — that ranking is the entire value of PIN=auto: 40 * - legacy text: the same triples with no header records. It cannot be validated, so it 41 * is accepted as-is, exactly as before. 42 * - inkling's "IKU1": uint32 {magic, n_layers, n_experts} then a dense 43 * uint32[n_layers][n_experts] block. Only inkling reads it; any other engine refuses 44 * it by name rather than loading another model's ranking.
legacy 文本:同样的三元组但没有头记录。无法校验,原样接受。
inkling 的 IKU1:uint32 {magic, n_layers, n_experts} 头 + 稠密 uint32[n_layers][n_experts] 块(magic = 0x31554B49,即 ASCII "IKU1")。只有 inkling 自己读;别的引擎按名字拒绝,不会误加载另一个模型的热度排名——这非常关键,因为不同模型的专家语义完全不同,混用排名会让热存选错专家。
注释第 39 行那句点睛:"that ranking is the entire value of PIN=auto"——这些积累的历史排名,正是 PIN=auto 学习型热存的全部价值来源。所以无论格式怎么演进,旧历史都不能丢,必须能读。
💡 深潜要点:route_trace.h 的格式设计是"约束驱动设计"的典范——负层号是为了
l>=0守卫,engine_id 是 hash 是因为字符串会静默截断,engine_id 在第三字段是因为%d的 INT_MAX UB。每一个看起来古怪的决策背后,都有一个具体的 fscanf/CRT/整数范围约束在逼着它这么定。
.coli_usage:文本 <layer> <expert> <count> 稀疏,负层号头 -1/-2 记维度和引擎身份。l>=0 守卫自然丢弃头记录,文件能在老引擎直接加载。%d 会 UB);legacy/IKU1 旧格式也读,IKU1 按名字拒绝跨引擎。下一节:格式设计讲完了,我们看 ROUTE_TRACE 流和
.coli_usage学习型缓存——路由遥测怎么变成"越用越快"的热存,以及"路由结构可缓存"是整个权重 JIT 的可行性基础。