第 3 章 · 02 LRU 缓存与一层前瞻预取 本节摘要:本节深潜 的第一个具体实现——每层 LRU 缓存 + 一层前瞻预取。19456 个专家放磁盘 370GB,Colibrì 给每个 MoE 层(共 75 层)维护一个独立的 LRU 缓存: 命中就直接返回 lease、未命中就从磁盘流式加载(走 st.h 的范围切片)。重叠机制是一层前瞻预取——router 线程跑在主计算前一层,用 把下一层会命中的专家提前加载。 实测:路由一层提前 71.6% 可预测。本节讲清 LRU 的命中/未命中路径、prefetch 的 advisory 语义(不持 lease、不驱逐活跃 slot)、 八指标、以及"为什么是一层而不是多层"的延迟与准确率权衡。读完本节,你理解了权重 JIT 的运行时机制。
本节摘要:本节深潜
ColiExpertStore的第一个具体实现——每层 LRU 缓存 + 一层前瞻预取。19456 个专家放磁盘 ~370GB,Colibrì 给每个 MoE 层(共 75 层)维护一个独立的 LRU 缓存:lookup命中就直接返回 lease、未命中就从磁盘流式加载(走 st.h 的范围切片)。重叠机制是一层前瞻预取——router 线程跑在主计算前一层,用prefetch把下一层会命中的专家提前加载。README.md实测:路由一层提前 71.6% 可预测。本节讲清 LRU 的命中/未命中路径、prefetch 的 advisory 语义(不持 lease、不驱逐活跃 slot)、ColiExpertStoreStats八指标、以及"为什么是一层而不是多层"的延迟与准确率权衡。读完本节,你理解了权重 JIT 的运行时机制。
内容来源:原项目源码
c/expert_store.h、README.md("Never wait for the disk twice" 段,71.6% / PILOT=1 / PIPE=1)。
⚠️ 注意:一层前瞻预取是 advisory(建议性)的——
expert_store.h:64-65明说 "Prefetch is advisory. Unsupported or rejected requests return zero."。这意味着 prefetch 失败、被拒绝、或资源不够时,引擎不会报错,只是该专家没被预取到——后续 lookup 会走未命中路径,慢一点但不影响正确性。这与第 1 章 03 节"放置只决定速度"一脉相承:预取是加速手段,失败只降速。
阅读完本节,你应当能够:
ColiExpertStoreStats 八指标的分组(命中率/预取效果/I/O 量/内存占用)。第 1 章 01 节讲过,19456 个专家分 75 个 MoE 层,每层 256 个专家。Colibrì 给每层维护一个独立的 LRU 缓存,而不是全局一个。这条设计有几个理由:
ColiExpertStore 抽象让这种"每层一个 store"很自然——引擎代码持有 75 个 ColiExpertStore *,每层一个,各自独立 lookup/release/prefetch。这是 expert_store.h 抽象层的第一个受益点。
lookup 是 ColiExpertStoreOps 的核心函数(expert_store.h:60-62 的虚函数定义)。LRU 实现里有两条路径:
lookup(layer=L, expert=E) ├── 命中(LRU 里有这个专家) │ ├── 提升 LRU 节点到队头(刚用过) │ ├── 填 ColiExpertView(gate/down/up 指向缓存里的 tensor,lease 指向 LRU 节点) │ ├── stats.requests++ / stats.hits++ │ └── 返回 0(view 有效到 release) └── 未命中(LRU 里没有) ├── 决定驱逐谁(LRU 尾部,且没有活跃 lease —— 见第 3 章 01 节铁律 6) ├── 从磁盘流式加载(走 st.h 范围切片,读 gate/down/up 三段) ├── 填入新 LRU 节点(队头) ├── 填 ColiExpertView(lease 指向新 LRU 节点) ├── stats.requests++ / stats.misses++ / stats.bytes_read += 加载字节 / stats.resident_bytes += ... └── 返回 0(view 有效到 release)
两条路径的关键差别是是否触发磁盘 I/O。命中路径零 I/O(纯内存操作);未命中路径要读磁盘(约 19MB/专家,int4 下)。这正是第 1 章 03 节"place 决定速度"的具体体现——命中就快(纯内存),未命中就慢(等磁盘)。
注意 LRU 驱逐的关键约束:只能驱逐没有活跃 lease 的 slot(expert_store.h:54-55 明说 prefetch "must not evict a slot that still has an active lease";同理 lookup 的驱逐也受 lease 契约管制)。这是 lease 契约的核心保障——持 lease 的专家绝不会被驱逐,保证它在使用期间数据完整。
未命中读磁盘是慢的。Colibrì 的应对是一层前瞻预取——让 router 线程跑在主计算前一层,提前算出下一层会激活哪些专家,用 prefetch 把它们预取到缓存。README.md "Never wait for the disk twice" 段实测:
a router-lookahead thread (
PILOT=1) prefetches the next layer's experts — routing is measurably 71.6% predictable one layer ahead.
71.6% 这个数字是"一层提前预取的命中率上限"——router 用前一层的结果预测下一层会激活的专家,约 71.6% 能猜对。预取命中的那 71.6%,下一层的 lookup 就走命中路径(零 I/O);剩下 28.4% 走未命中路径(读磁盘)。
预取的工作流可以这样画:
时间轴 ──────────────────────────────────────────────► 主计算线程: [算层 L0] [算层 L1] [算层 L2] ... PILOT 线程: [预测 L1 专家, prefetch] [预测 L2 专家, prefetch] [预测 L3 专家, prefetch] 异步 I/O 池: [后台拉 L1 预取的专家] [后台拉 L2 预取的专家]
主计算线程算层 L0 时,PILOT 线程已经在预测 L1 的专家并 prefetch;异步 I/O 池在后台拉这些专家。等主线程算到 L1 时,L1 的预取专家大概率已经到位(命中路径)。这就是"重叠(overlap)"——把磁盘 I/O 藏在 compute 后面,主线程几乎不阻塞等磁盘。
注意三个开关的区别:(1) PILOT=1 启用 router-lookahead 预取线程(一层前瞻);(2) PIPE=1(默认)启用有界异步 I/O 池,加载缺失专家与 resident 专家计算重叠;(3) COLI_CUDA_PIPE=2 在 GPU 上保持 residual stream 跨层不离开设备。三者协同把"读磁盘"这件事最大化隐藏。
expert_store.h:56-57 和 expert_store.h:64-65 反复强调 prefetch 的 advisory 性质。完整语义四条:
expert_store.h:56 明说 "holds no lease"。这意味着 prefetch 后,该 slot 可以被后续的 lookup 驱逐(如果还没被 lookup 拿走 lease 的话)。预取只是"建议缓存把数据加载进来",不锁定。expert_store.h:56-57 明说 "must not evict a slot that still has an active lease"。prefetch 即使触发驱逐(为预取腾空间),也绝不能驱逐持活跃 lease 的 slot——这与第 3 章 01 节铁律 6 一致。expert_store.h:64 注释 "Unsupported or rejected requests return zero."——返回 0 只代表"无错",不代表"已成功预取到内存"。具体实现可以静默忽略 prefetch 请求。这套语义的设计目的是:让 prefetch 永远不破坏 lease 契约,也永远不让引擎因预取失败而崩溃。预取失败只意味着"下一层这个专家可能要未命中",降速但不影响正确性。
💡 深潜要点:prefetch 的 advisory 语义是 Colibrì "诚实研究"在缓存层的体现。它不承诺预取一定生效(返回 0 只是"无错"),也不承诺预取的数据一定留下来(不持 lease,可被驱逐)。这种"尽力而为"的接口让缓存实现有最大自由度(可以根据当前内存压力决定要不要预取),也让引擎代码不需要为预取失败兜底——失败就退化成未命中,语义不变。
expert_store.h:29-38 的八个指标,在第 3 章 01 节列过,这里从"衡量什么"的角度重组:
| 指标 | 衡量什么 | 怎么算 |
|---|---|---|
requests |
总 lookup 次数 | 每次调用 lookup ++ |
hits |
缓存命中数 | 命中路径 ++ |
misses |
缓存未命中数 | 未命中路径 ++ |
| 命中率 | 缓存效果 | hits / requests |
prefetched |
prefetch 实际预取数 | prefetch 实际加载的专家数 ++ |
prefetch_hits |
预取命中数 | lookup 时发现"这个专家是预取来的"++ |
| 预取命中率 | 预取准确度 | prefetch_hits / prefetched |
bytes_read |
磁盘 I/O 总量 | 每次未命中加载的字节累加 |
resident_bytes |
当前缓存占用 | 加载时 ++,驱逐时 -- |
capacity_bytes |
缓存总容量 | 初始化时定 |
注意两个派生指标:命中率 = hits/requests(衡量缓存整体效果)、预取命中率 = prefetch_hits/prefetched(衡量一层前瞻的准确度)。README.md 的 71.6% 就是预取命中率的理论上限——实际 prefetch_hits/prefetched 会接近这个数。
bytes_read 是磁盘压力的直接量化——它告诉你这个工作负载从磁盘读了多少字节。第 5 章双 SSD 带宽翻倍时,这个指标会用来对比"一盘 vs 两盘"的 I/O 分布。resident_bytes/capacity_bytes 衡量缓存饱和度——resident 接近 capacity 意味着缓存满了,驱逐频繁,可能需要扩容或换策略。
一个自然的问题:既然一层前瞻能预取,为什么不多层(两层、三层)?这是延迟与准确率的权衡:
Colibrì 选一层是当前实测的最佳点:71.6% 的准确率足够高,且一层窗口刚好能让异步 I/O 藏在 compute 后面。多层预取在 README.md 的开放假设表里没有单独列出,但属于"路由感知推测"那行的延伸——它是一个可证伪假设,如果有跨硬件端到端 A/B 证明多层更优,就可以默认开。目前一层是"已实现、默认开(PILOT=1)"的状态。
⚠️ 注意:
README.md的开放假设表"路由感知推测"那行明说:"MTP has also measured a 32% loss around 85% expert hit"——也就是在约 85% 专家命中率附近,MTP 推测解码测得 32% 损失。这暗示预取/推测类机制在高命中率时反而可能亏(验证开销超过加速收益)。这是一层预取也面临的边界——它不是单调赢的优化,在工作负载/硬件变化时可能负收益,所以必须可关(PILOT=0)。
ColiExpertStore,工作集天然分层、容量独立预算、隔离驱逐。PILOT=1 router-lookahead、PIPE=1(默认)异步 I/O 池、COLI_CUDA_PIPE=2 GPU residual stream 跨层。下一节,我们看 Colibrì 的"学习型"缓存——
.coli_usage怎么记录每 turn 的路由历史,pinned hot-store 怎么把最热专家钉在 RAM/VRAM,以及为什么"越用越快"是个可能过拟合的可证伪假设。