第 3 章 · 01 ColiExpertStore 抽象与 lease 租约契约


文档摘要

第 3 章 · 01 ColiExpertStore 抽象与 lease 租约契约 本节摘要:本节深潜 Colibrì 的权重 JIT 核心—— (99 行)。它定义了"专家流式缓存"的抽象接口: 定位一个专家(layer, expert), 是它的可读视图(gate/down/up 三个 tensor + 一个 lease 句柄)。核心是 lease 租约契约—— 成功后必须 恰好一次, 不可 copy, 时 debug 构建会断言零活跃 lease。这套契约保证"一个专家在被用期间不会被缓存驱逐"。所有具体实现(LRU 缓存、学习型热存)通过 虚函数表接入——这是 expertstore.h 唯一允许的"虚函数"风格,与第 1 章 02 节"纯 C 无引擎依赖"形成精妙平衡。

第 3 章 · 01 ColiExpertStore 抽象与 lease 租约契约

本节摘要:本节深潜 Colibrì 的权重 JIT 核心——c/expert_store.h(99 行)。它定义了"专家流式缓存"的抽象接口:ColiExpertKey 定位一个专家(layer, expert),ColiExpertView 是它的可读视图(gate/down/up 三个 tensor + 一个 lease 句柄)。核心是 lease 租约契约——lookup 成功后必须 release 恰好一次,ColiExpertView 不可 copy,destroy 时 debug 构建会断言零活跃 lease。这套契约保证"一个专家在被用期间不会被缓存驱逐"。所有具体实现(LRU 缓存、学习型热存)通过 ColiExpertStoreOps 虚函数表接入——这是 expert_store.h 唯一允许的"虚函数"风格,与第 1 章 02 节"纯 C 无引擎依赖"形成精妙平衡。读完本节,你理解了 Colibrì 权重 JIT 的契约骨架。

内容来源:原项目源码 c/expert_store.h(99 行,完整精读)、c/tensor.h(47 行,ColiTensorView 定义)。

⚠️ 注意:线程安全是 implementation-specific——expert_store.h:52-55 明说,调用者不能假设 lookup/release/prefetch/stats/destroy 在同一 store 上并发安全,除非具体实现文档保证。views 绝不能从多线程并发用。这是 Colibrì 把并发责任推给具体实现(LRU/学习型热存各自决定加不加锁)的设计选择。

学习目标

阅读完本节,你应当能够:

  1. 解释 expert_store.h 为什么是 Colibrì 的"权重 JIT 核心"。
  2. 读懂 ColiExpertKey(layer, expert)与 ColiExpertView(key/gate/down/up/lease)。
  3. 复述 lease 租约契约的五条规则(必须 release 一次、不可 copy、不可重入 lookup 等)。
  4. 理解为什么 destroy 在 debug 构建里断言零活跃 lease。
  5. 说出 ColiExpertStoreOps 虚函数表的五个函数(lookup/release/prefetch/stats/destroy)。
  6. 理解 ColiExpertStore{ops, state} 的两字段结构(虚函数表 + 不透明状态)。
  7. 读懂 coli_expert_lookup/coli_expert_release 内联包装的容错逻辑。

一、expert_store.h 的角色:权重 JIT 核心

第 1 章 01 节讲过,Colibrì 把 19456 个路由专家放磁盘(~370GB)按需流式加载,只把热点专家留在高速层。这个"按需流式加载 + 缓存"的全部抽象,就是 expert_store.h。它是一份接口契约,不是具体实现——具体实现(每层 LRU 缓存、学习型固定热存)各自实现这套接口,接入引擎。

expert_store.h:1-12 的开头是标准的 include guard 和 C++ extern 包装:

1 #ifndef COLIBRI_EXPERT_STORE_H 2 #define COLIBRI_EXPERT_STORE_H 3 4 #include <stddef.h> 5 #include <stdint.h> 6 #include <string.h> 7 8 #include "tensor.h" 9 10 #ifdef __cplusplus 11 extern "C" { 12 #endif

它 include tensor.h(第 2 章 02 节讲过 ColiTensorView),因为专家视图里的 gate/down/up 三个 tensor 是 ColiTensorView 类型。string.h 是为了内联包装里的 memset(下面会看到)。

二、ColiExpertKey 与 ColiExpertView

expert_store.h:14-27 定义两个核心数据结构:

14 typedef struct ColiExpertStore ColiExpertStore; /* 不透明前置声明 */ 15 16 typedef struct { 17 int layer; /* 哪一层(0..74) */ 18 int expert; /* 该层的第几个专家(0..255) */ 19 } ColiExpertKey; 20 21 typedef struct { 22 ColiExpertKey key; 23 ColiTensorView gate; 24 ColiTensorView down; 25 ColiTensorView up; 26 void *lease; /* 租约句柄,实现内部使用 */ 27 } ColiExpertView;

ColiExpertKey 是专家的"坐标"——(layer, expert) 二元组,定位 19456 个专家里的一个。注意它不包含 MTP 头的 layer 编号约定——具体编码留给出层(75 MoE 层 + MTP 头)。

ColiExpertView 是"一个专家的可读视图"。MoE 专家内部是三个矩阵:

  • gate:门控投影(第一个线性层)。
  • down:降维投影(往往是 expert 的瓶颈维度)。
  • up:升维投影(第三个线性层)。

(具体 MLP 拓扑因模型族而异,GLM 的 SwiGLU 是 gate/up 并行后激活再 down,但 expert_store.h 抽象层不关心这些——它只承诺"给你这三个 tensor 的视图,lease 有效期内可读"。)三个视图都是 ColiTensorView(tensor.h:30-41),里面带 format/scale_format/data/scales/data_bytes/rows/columns 等——这是第 2 章 02 节 quant.h 解码所需的全部信息。

第五个字段 void *lease租约句柄,类型故意用 void *——具体实现(LRU/学习型热存)把它指向自己的内部结构(如 LRU 节点指针)。抽象层不解释它,只承诺"release 时把它和 view 一起清零"。

三、lease 租约契约:五条铁律

expert_store.h:40-58 的注释是 expert_store.h 的灵魂——lease 租约契约。把英文注释提炼成五条铁律:

五条铁律逐条拆:

  1. lookup 成功后必须 release 恰好一次(exactly once,不是引用计数)。配套约束:不可 copy ColiExpertView(lease 不可共享);不可把已有活跃 lease 的 view 传给 lookup(会被误覆盖)。
  2. lookup 失败时 view 被清零,调用者既不能用也不能 release(与 coli_expert_lookup 包装的容错逻辑呼应)。
  3. release 清整个 view,且对已清零或零初始化的 view 是 no-op——重复 release 安全。
  4. destroy 要求零活跃 lease(debug 构建断言)——销毁前所有 lease 必须先 release,防 dangling 指针。
  5. 线程安全是 implementation-specific——调用者不能假设并发安全,views 绝不能从多线程并发用。并发责任推给具体实现。

第 6 条关于 prefetch:它是 advisory(建议性),不持 lease,且不能驱逐仍持活跃 lease 的 slot——这条与第 3 章 02 节的"预取不驱逐活跃 slot"直接挂钩。

💡 深潜要点:lease 契约的核心目的是保证一个专家在被用期间不会被驱逐。设想没有 lease:LRU 缓存可能在你读 gate 矩阵时,把 down/up 矩阵驱逐了——因为 LRU 不知道你"正在用这个专家"。lease 把"我正在用"显式化:lookup 拿 lease → 用 gate/down/up → release 还 lease;持 lease 期间,缓存实现承诺不驱逐这个专家的 slot。这是权重 JIT 能"语义不变"的关键保障——专家数据在被读期间必须完整、稳定。

四、ColiExpertStoreOps:虚函数表

expert_store.h:59-69 定义虚函数表 ColiExpertStoreOps,这是 expert_store.h 唯一允许的"面向对象"风格:

59 typedef struct { 60 /* Returns zero on success. The view remains valid until release(). */ 61 int (*lookup)(ColiExpertStore *store, ColiExpertKey key, 62 ColiExpertView *view); 63 void (*release)(ColiExpertStore *store, ColiExpertView *view); 64 /* Prefetch is advisory. Unsupported or rejected requests return zero. */ 65 int (*prefetch)(ColiExpertStore *store, const ColiExpertKey *keys, 66 size_t count); 67 void (*stats)(const ColiExpertStore *store, ColiExpertStoreStats *stats); 68 void (*destroy)(ColiExpertStore *store); 69 } ColiExpertStoreOps;

五个函数指针,每个都是"具体实现要提供的回调":

  • lookup:查一个专家。成功返回 0,view 有效直到 release;失败返回非 0,view 被清零。
  • release:释放一个 lease,清整个 view。
  • prefetch:预取一组专家(keys 数组 + count)。advisory——不支持或被拒绝的请求返回 0(注意:prefetch 返回 0 不代表"已加载",只代表"无错")。
  • stats:填一个 ColiExpertStoreStats(第 3 章 02 节详讲八指标)。
  • destroy:销毁 store。debug 构建会断言零活跃 lease。

这套虚函数表是 expert_store.h 与具体实现(LRU/学习型热存/全驻留)的契约边界。每个具体实现写一份 ColiExpertStoreOps,把函数指针填上自己的实现,然后通过 ColiExpertStore 暴露给引擎。这与 C++ 的 vtable 同构,但用纯 C 函数指针实现——这是第 1 章 02 节"纯 C 零依赖"与"需要多态"之间的折衷。

五、ColiExpertStore:ops + 不透明 state

expert_store.h:71-74 定义 store 本体,只有两个字段:

71 struct ColiExpertStore { 72 const ColiExpertStoreOps *ops; /* 虚函数表,指向具体实现的 ops */ 73 void *state; /* 不透明状态,具体实现自由解释 */ 74 };
  • ops:指向具体实现的 ColiExpertStoreOpsconst 表示虚函数表本身不可变(典型用法是每个具体实现有一份 static const 的 ops)。
  • state:void *,具体实现的内部状态(LRU 实现里指向 LRU 表;学习型热存里指向热存 + 历史记录;全驻留实现里可能就是 NULL)。抽象层不解释。

这种"ops + state"模式是 C 里实现多态的标准手法——等价于 C++ 的 vtable + this,但 this 被显式拆成"虚函数表指针"和"数据指针"两部分。它的好处是:引擎代码只用 ColiExpertStore * 抽象指针,不需要知道具体实现类型。换缓存策略(LRU → 学习型)只换 ops 和 state,引擎代码不变。

六、ColiExpertStoreStats:八指标速览

expert_store.h:29-38 定义统计结构,共八个字段,这里速览(第 3 章 02 节详讲):

分组 字段 含义
命中率 requests/hits/misses 总请求/命中/未命中,命中率 = hits/requests
预取效果 prefetched/prefetch_hits 预取数/预取命中数,准确度 = prefetch_hits/prefetched
I/O 量 bytes_read 从磁盘读的字节,衡量磁盘压力
内存占用 resident_bytes/capacity_bytes 当前常驻/总容量,衡量饱和度

这些指标是 Colibrì "诚实研究"在缓存层的落地——每个数字都可观测、可 A/B,用户/贡献者能直接据此判断缓存策略是否有效。具体含义和派生用法在第 3 章 02 节展开。

七、内联包装:容错与清零

expert_store.h:76-93 提供两个 static inline 包装,它们是引擎代码实际调用的入口:

76 static inline int coli_expert_lookup(ColiExpertStore *store, 77 ColiExpertKey key, ColiExpertView *view) { 79 if (!store || !store->ops || !store->ops->lookup) { 80 if (view) memset(view, 0, sizeof(*view)); return -1; } 83 int result = store->ops->lookup(store, key, view); 84 if (result != 0 && view) memset(view, 0, sizeof(*view)); /* 失败兜底清零 */ 85 return result; 86 } 88 static inline void coli_expert_release(ColiExpertStore *store, ColiExpertView *view) { 90 if (store && store->ops && store->ops->release) store->ops->release(store, view); 92 if (view) memset(view, 0, sizeof(*view)); /* release 后清零 */ 93 }

注意三层容错:(1) 入口检查 store/ops/ops->lookup 都非空,否则清零 view 返回 -1(让"store 没初始化"也能优雅失败);(2) 调具体实现的 lookup,失败时(result != 0)再清零一次 view(双保险,即使具体实现忘了清,包装层兜底);(3) coli_expert_release 即使 store/ops/release 为空,也照样清零 view——这让"对一个从未 lookup 成功的 view 调 release"也安全(no-op)。这套包装让引擎代码可以放心写 if (coli_expert_lookup(...) == 0) { 用 view; coli_expert_release(...); },失败路径不需要 release,容错和清零全部在包装层兜底。

⚠️ 注意:coli_expert_release 末尾的 memset(view, 0, ...)(expert_store.h:92)是 lease 契约的关键执行点——它确保 release 后 view 不可再用(任何字段都归零),从根上杜绝"use-after-release"。这是 Colibrì 用纯 C 实现 RAII 风格资源管理的典型手法。

本节要点回顾

  1. expert_store.h 是权重 JIT 核心:19456 个专家按需流式加载 + 缓存的全部抽象。
  2. ColiExpertKey(layer, expert):专家坐标;ColiExpertView(key/gate/down/up/lease):专家可读视图。
  3. lease 租约契约五铁律:lookup 成功必须 release 恰好一次、不可 copy、不可重入 lookup、失败不 release、destroy 要求零活跃 lease。
  4. lease 的目的:保证专家在被用期间不被驱逐——这是权重 JIT 语义不变的关键。
  5. ColiExpertStoreOps:五函数虚函数表(lookup/release/prefetch/stats/destroy),纯 C 函数指针实现多态。
  6. ColiExpertStore{ops, state}:ops 虚函数表 + state 不透明状态,典型 C 多态模式。
  7. 内联包装容错:coli_expert_lookup/release 三层容错 + 失败/释放后清零,实现 RAII 风格资源管理。

下一节,我们看 expert_store.h 的第一个具体实现——每层 LRU 缓存,以及"一层前瞻预取"怎么用 prefetch 把磁盘 I/O 藏在 compute 后面。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U