OLMo-core 3:MoE 训练栈让专家池越大、吞吐不再越慢
混合专家(Mixture of Experts,MoE)把两件看似矛盾的事捏在了一起:模型越大,专家池越大;每个 token 却只路过一小撮专家。按直觉,专家池翻上十倍,每个 token 的算术量几乎不变,训练速度理应不受影响。Ai2(Allen Institute for AI)在 2026 年 10 月发布的 OLMo-core 3 训练栈,给出了一套把这条直觉拆穿、再把它改写掉的完整论证。这篇专题沿着「为什么更慢 → 税藏在哪 → 怎么免税 → 还剩下什么税 → 数字的边界」这条因果链往下走。
每个 token 只算一小撮参数,为什么专家池做大反而更慢?
直觉的破绽:算得少不等于跑得快
MoE 的卖点在于稀疏激活:每个 token 只触发 top-k 个专家,激活参数量(active parameters)与总参数量脱钩。于是「总参数从 4.6B 涨到 47B,而每个 token 激活的参数固定在约 3.2B」听起来应该是免费的。既然每个 token 的浮点运算量(FLOPs,floating-point operations)几乎不变,训练成本理应不变。
但墙钟时间(wall time,实际跑完要多久)不由 FLOPs 单独决定。一个 kernel 跑多快,取决于两种资源谁先不够用:算力,还是带宽。Roofline 模型(Williams、Waterman 与 Patterson 2009 年在 CACM 提出的性能分析框架)把这件事画成一幅二维图:横轴是算术强度(operational intensity),即每从内存搬 1 字节能换来多少次浮点运算;纵轴是可达成的浮点吞吐。可达吞吐是「峰值算力」与「峰值带宽 × 算术强度」两者中较小的那个,两条线的交点叫拐点(ridge point),拐点左侧是带宽受限区,右侧是算力受限区。Roofline
$$\text{attainable} = \min(\text{peakCompute},\ \text{peakBandwidth} \times \text{intensity})$$
关键在最左边这一半:在带宽受限区,减少 FLOPs 只是让横轴往左移,天花板的高度不变。运算量砍掉了,但只要还在被带宽卡住,吞吐纹丝不动,时间照样由「字节数 ÷ 带宽」决定。MoE 的专家 FFN 恰恰是低算术强度的算子,每个专家算得少,却要先把 token 行从别处搬过来。所以「active FLOPs 没变」并不足以推出 wall time 不变:FLOPs 不变只说明分子未增,不能说明算术强度不变;若为专家权重或激活搬了更多字节,横轴反会左移,因此不能由 active FLOPs 推出 wall time。

图注:Roofline 模型的标准图示。横轴是算术强度(Flops/Byte),纵轴是可达浮点吞吐。峰值算力水平线(16 GFlops/sec)与峰值带宽斜线在拐点 (1, 16) 处相交;红色虚线标出带宽受限区(x≈0.5)与算力受限区(x=2)。左半边就是「减 FLOPs 不省时间」的区域。注意这张图是 2008 年 Opteron X2 实例,框架适用于 GPU,具体数值不可搬用。Roofline
谜题:同样的专家池,新旧两条栈的吞吐曲线分道扬镳
OLMo-core 3 技术报告用一张图把「专家池变大反而更慢」这件事钉成了必须解释的谜题。在 8× B300 单节点、8 层 BF16 模型、top-4 路由、EP8 的设定下,把专家总容量从 dense 3.2B 一路扩到 MoE 3.2B@47B:旧栈(full-reshard FSDP)的吞吐从 61 跌到 19.4 K tok/s/GPU,大约掉了 68%;新栈从 63 掉到 52 K,跌幅远小于旧栈。OLMo-core 3

图注:双系列柱状图,横轴从 Dense 3.2B 到 MoE 3.2B@47B,纵轴是 K tok/s/GPU。旧栈柱子一路下滑(61 / 50.5 / 41.2 / 31.6 / 19.4),新栈几乎持平(63 / 54.5 / 53.3 / 52.8 / 52)。这是 8 层、单节点、8× B300 的 preliminary 测量,而且是新旧两条完整链路的 composed-system 对比,不是把 FSDP 与 DDP 单独拎出来的受控消融。OLMo-core 3
专家池大了约十倍,active FLOPs 几乎不动,旧栈却越来越慢,新栈几乎不受影响。同一条曲线上两套并行策略的分道扬镳,就是整篇专题要解释的东西。故意把它摆在这里当谜题,是因为它的答案不在「算得多不多」,而在「搬得多不多、同步等多久」。这正是下一节要去看的:旧栈到底在哪儿反复搬东西。
这笔税藏在哪一环:FSDP 为什么每个 microbatch 都要重搬一遍专家权重?
full-reshard FSDP 的「按需拼回、用完即扔」
旧栈用的是 full-reshard FSDP(全分片数据并行)。FSDP 把模型切成若干 unit,每个 unit 是一组相邻层参数拼成的大张量,每个 rank 只持有每个 unit 的一份分片。进入某个 unit 的 forward(前向)之前,先做一次 all-gather(全收集),把各 rank 上的分片拼回完整的 unit 参数;算完立刻 free(释放),下一个 unit 再来一遍。backward(反向)里也一样,反向前再 all-gather 一次,反向后用 reduce-scatter(归约分散)同步梯度。FSDP
这套「按需拼回、用完即扔」是为稠密模型设计的省显存办法:任何时刻只有当前 unit 的完整参数驻留显存,其余都保持分片。FSDP 论文自己指出,与流水线并行(PP)联合使用时,默认 full sharding 要对每个 microbatch(微批)都 unshard 一遍模型参数,通信开销被显著放大。FSDP

图注:FSDP 论文的算法总览图,用六层稠密模型切成三个 FSDP unit 作示例:每个 unit 的参数被切片到多张 GPU 上,进入 unit 计算前先 all-gather 拼回完整参数,算完立刻释放,反向时 pre-backward 再 all-gather、post-backward 用 reduce-scatter 同步梯度。请注意这是稠密模型示例,图中没有 MoE,「容量税」这个词也不属于这张图。FSDP
同一节奏搬到 MoE,成了按专家池大小征收的税
把这条节奏搬到 MoE 上,问题就显形了。前文说过,FSDP 是以 unit 为单位物化参数的:一个 unit 把整层参数拼成一张大张量,而 MoE 层的专家池就装在这个 unit 里,因此这个 unit 的大小由总专家池决定,与当前 microbatch 命中了几个专家无关。每个 microbatch 进入这个 unit 时,都要把它整体 all-gather 成完整参数,算完再释放。路由在这里只决定每个 token 去命中哪几个专家、哪些 token 参与计算,它并不能让被物化的 unit 变小:无论本 microbatch 命中多少专家,拼回来的都是含整个专家池的那张张量。于是专家池越大,被物化的 unit 越大,每个 microbatch 要重复搬运的权重就越多,而每个 token 的 active FLOPs 并不随之增长。OLMo-core 3 把这种「跟着总参数量走、每个 microbatch 缴纳一次」的隐性成本命名为容量税(capacity tax)。OLMo-core 3
这里要严格分清两方的贡献:机制来自 FSDP 论文,命名权归 OLMo-core 3。FSDP 论文只描述 per-unit all-gather / free,从未讨论 MoE;是 OLMo-core 3 把这个机制推到 MoE 场景,观察到 active FLOPs 不变而搬运量随专家池线性增长,才给它起了名字。回到上一章的旧栈曲线:61 → 50.5 → 41.2 → 31.6 → 19.4 K,每一根下滑的柱子,都是这笔税在不同容量档位上的读数。那么问题自然变成:能不能干脆别搬权重?
怎么让税停下来:权重常驻、token 去找专家,四件套各管一层
核心反转:把搬运单位从权重换成 token 行
旧栈的隐含动作是「搬权重去找计算」:谁需要参数,就把参数搬过去。OLMo-core 3 的做法正好反过来:「搬 token 行去找权重」。专家分片常驻在 GPU 上不挪窝,被路由命中的 token 行被送到权重所在的 rank。搬运量的计量单位从「随总参数量增长的专家权重」换成了「随实际激活量增长的 token 行」,这正是 1.2T 模型也只激活 58.36B 参数的底气来源。Ai2 官方博客用一句话概括了这个反转:让专家常驻 GPU,把相关数据路由过去,避免反复聚合权重。Ai2 blog
四件套各管一层
架构反转不是一个动作,而是四个动作的分工,各管一种「该驻留还是该搬运」的对象:
- DDP 常驻分片:每个 rank 在整个 accumulation window(梯度累积窗口)内持有自己的模型分片,权重不释放,梯度等到 optimizer step 才同步一次。它管的是「权重」。
- 专家并行(EP):把被路由的专家分片到各 rank 之间,只把命中的 token 行送到对应 rank。它管的是「激活行」。
- 分布式优化器:把 FP32 主权重和优化器状态分片到各 rank。它管的是「状态」。
- 流水线并行(PP):单 rank 装不下模型时,把层切到不同 rank 组。它管的是「层」。

图注:OLMo-core 3 训练栈的子系统分层图。顶部是配置与编排层,中间是 MoE 模型架构、多组 DDP/EP/PP 运行时、精度与 FP8 运行时(含优化器状态布局、激活控制、torch.compile、对称内存后端、checkpointing),底部是底层支持(自定义 CUDA/C++/Triton 与 CUTLASS grouped GEMM)。这是一张按层堆叠的能力地图,没有箭头,表达「哪个子系统负责哪块能力」,不表达数据流向。OLMo-core 3
有了这张地图,前面讲的四件套各有归宿:MXFP8 住在精度与 FP8 运行时里,recompute 住在激活控制里,EP 与 NVSHMEM 住在多组运行时里,grouped GEMM 住在底层支持的 CUTLASS 里。
这条线不是 2026 年才长出来的
「专家分片到不同 device」这个概念的最早落点是 GShard(Google,2020)。GShard 在每隔一层的 FFN 位置插入 MoE 层,把专家池绑到 device 数:编号为 e 的 expert 恒放在第 e 个 device 上,其余层 replicated(复制到所有 device)。GShard

图注:三栏示意。(a) 标准 Transformer encoder,(b) 每隔一层的 FFN 替换为带 gate 路由的 MoE 层,(c) 扩展到多 device 时 MoE 层的 expert 切到不同 device,其它层保持 replicated。这就是 2020 年 EP 概念的原貌;注意它不是 OLMo-core 3 的 DDP+EP 架构,两者的并行分工并不相同。GShard
GShard 的非 MoE 层走 replicated,不是 DDP;OLMo-core 3 是显式 DDP + EP 分工,两者是同一精神的精细化后代,不是同一实现。把 DDP/EP 放进五维并行地图的是 MoE Parallel Folding(Megatron 派系,2025):它点出一条传统约束,EP 通常嵌在 DP 的子组里,即 EP ≤ DP,EP 并行度被 DP 顶死,无法随专家池独立扩大;解法是让 attention 层和 MoE 层各用一套并行组,attention 用 TP×CP×DP×PP,MoE 层用 TP×EP×DP×PP,两层唯一共享的是 PP 维度。Parallel Folding
OLMo-core 3 选择 DDP + EP,在这个地图上的位置就清楚了:DDP 承担常驻分片,EP 独立于 DP 扩展,attention 与 MoE 之间不必互相迁就。这一章解决的是「谁跟谁通信」的粗粒度问题。下一章要问的是更细的问题:路由这一下,通信本身还有没有可省的?
换了并行策略还不够:路由这一步还藏着一笔搬运税
块级 all-to-all 的三段式:打包、交换、置换
EP 把 token 行送去专家所在的 rank,传统实现是块级 all-to-all(全对全通信):先把命中的 token 按目标 rank 打包成方块,发起 all-to-all 交换,再做全局置换(global permute),把行排成专家 GEMM 需要的顺序;算完反向再来一遍。打包与置换都是额外的布局变换,每一步还得经过主机协调。
rowwise EP:用 NVSHMEM 一端直写
OLMo-core 3 的 rowwise EP(行式专家并行)不做打包。它直接查路由表,把每一行用 NVSHMEM(NVIDIA 的 GPU 间对称内存通信库)一端直写进目标专家的输入缓冲区,GPU 到 GPU,不经主机。机制级证据来自报告 Table 7 的单次 trace:dispatch 阶段,块级 all-to-all 的三个子步骤合计 2.843 ms,行式合并为 2.298 ms;combine 从 3.073 ms 降到 2.423 ms。OLMo-core 3
另外两处税:主机同步与 padding
并行策略只决定「谁跟谁通信」,而下面两条决定「这趟通信要不要等主机、要不要做多余功」。
第一,GPU 常驻路由元数据。原来 all-to-all 的 split sizes(每个目标拿多少 token)和专家 GEMM 的 M 维度(每个专家算多少行)都要 CPU 算好再回拷给 GPU;现在路由计数、expert 行数、per-expert 置换全部由 device 端生成的 route map 表达,两处 host sync(主机同步)被消除。OLMo-core 3
第二,设备端 grouped GEMM。把多个小专家 GEMM 合并成一次 kernel 调度,按 route map 里的精确行数发起计算,不再把每个专家 pad(补零)到统一的 capacity。省掉的既是 padding 浪费的 FLOPs,也是主机逐次发起小 kernel 的等待。
DeepEP 是什么角色
DeepEP 是 DeepSeek 开源的 EP 通信库,把这些机制在 inter-node 路径上做成了可执行参照。它的 normal kernel 把 dispatch 与 combine 组织成 GPU 发起的流水线,dispatch/combine 内部把数据切分成 IB chunk(走 InfiniBand 跨节点)与 NVL chunk(走 NVLink 节点内),布局信息在 dispatch 与 combine 之间复用。

图注:DeepEP normal kernel 的 CPU/GPU 两行流水线示意:CPU 侧依次 Launch notify、分配张量、发起 dispatch、发起计算、发起 combine;GPU 侧 Notify 之后是 Dispatch(内部切 IB 与 NVL chunk)、计算 kernel、Combine(内部再按 NVL 与 IB chunk 合并)。这是流程示意,不是性能图,图里没有任何带宽数字。DeepEP
需要划清边界:OLMo-core 3 的主路径是自家实现,DeepEP v2 只是实验过的可选 inter-node backend,在 2.38T 配置上跑的是 short-capacity test,不是生产默认,也不代表持续训练吞吐。Ai2 blog
到这一步,「怎么运」已经被优化了一遍。还剩最后一个维度:运的时候,能不能让字节本身变少?
MXFP8 压的不只是矩阵乘,而是计算、激活、传输三处一起压
一个低精度格式,同时压三个地方
前面的手段都在改「搬什么、怎么搬」,MXFP8(Microscaling FP8)直接改「搬多少字节」。它的思路是:用 8-bit 的 FP 元素(E4M3)表示数值,每 32 个元素共享一个 E8M0 的 scale(缩放因子)。E8M0 只有指数位、没有尾数位,只表示 2 的幂,所以 scale 可以直接从 FP32 表示的指数位里按位抽出,硬件不需要做浮点 scale 运算。MXFP8
关键在 scale 是 block-local(块内局部)的。每个 32 元素块自己算自己的 scale,块与块之间没有协调问题,因此不需要 scale-sync all-reduce,这是它区别于 FP8 Current/Delayed Scaling(整张张量共享单一 scale)的地方。scale 的计算只有三步:
448 是 E4M3 能表示的最大值,除以它再取 2 的幂,保证块内最大值刚好不溢出可表示范围,quantize-dequantize 确定且无状态。scale 每 32 元素一个,均摊下来每个元素约 8.25 bit,通信字节量几乎砍半,而且没有额外的 FP32 scale 通信开销。MXFP8
一处不落地同时用
OLMo-core 3 把 MXFP8 用在三条路径上:GEMM 用 MXFP8,反向要用的 saved activation 缓存也用 MXFP8,跨专家 dispatch 也用 MXFP8 直接传量化数据(避免 BF16 中转)。FP32 主权重仍然是权威副本,低精度只是中间表达,每个 optimizer step 之后从 FP32 主权重重新量化出 forward 与 backward 两个方向的 MXFP8 cache。OLMo-core 3
这不是 2026 年才有的新想法。DeepSeek-V3 技术报告(2024 年 12 月)已经在 671B MoE 上跑过同一思想:每个 Linear 算子的三个 GEMM(前向 Fprop、激活反向 Dgrad、权重反向 Wgrad)全部用 FP8,反向要用的激活缓存降成 FP8,MoE 跨节点的 dispatch 也在 up-projection 之前就量化成 FP8,而 embedding、gating、norm、attention 与主权重保留高位精度。DeepSeek-V3

图注:以单个 Linear 算子为中心的三位点压缩示意图:前向、激活反向、权重反向三个 GEMM 都是 FP8,反向缓存激活以 FP8 存储并喂回 Wgrad,MoE up-projection 前完成 FP8 量化。图中同时保留高位精度路径(embedding、输出头、gating、norm、attention、主权重、梯度与优化器状态)。这张图画的是 FP8 粒度(激活 1×128、权重 128×128 块),不是 MXFP8 的 32 元素块,两者同源但不可混比。DeepSeek-V3
工程数字与收益分布
4× B300 的受控对比里,开 MXFP8 后端到端训练吞吐比 BF16 高约 21%,峰值 active memory 从 103 GiB 降到 95 GiB;收益大半来自 FFN 计算与跨专家传输,而不是 attention。OLMo-core 3
这一步把「搬运的字节」实实在在压小了,正好落在全篇主线上:该省的地方在搬运,不在 FLOPs。但把这些数字当真之前,还有最后一件事要做:划清它们证明了什么、没证明什么。
这些数字到底证明了什么,又没证明什么?
所有 headline 数字都是随机路由下的系统基准
先把边界划清。47B 相对旧栈约 2.7×(19.4K → 52K tok/s/GPU)、专家容量扩约十倍吞吐下降不足 5%、MXFP8 约 +21%、1.2T 配置在 512 张 B300 上达到 858 useful-model TFLOP/s/GPU,全部是 Ai2 自家 B300 上、随机路由(random routing)下测得的系统基准,不是真实训练的收敛质量(time-to-quality)。报告 §14 自己写明:每个 headline rate 都是随机路由下测得的 systems reference,不是 time-to-quality 声明。OLMo-core 3

图注:约 20 种方法 × 约 27 个成本维度的定性矩阵,格子用 +(缓解)、−(引入开销)、▷(条件性)、□(无直接一阶效应)标注。caption 明确说这是「通常的一阶方向」,定性符号不预测端到端加速;同一个方法对不同成本维度往往一缓一增,没有银弹。OLMo-core 3
这张矩阵本身就是方法论上的诚实:单看一行,没有哪个方法在所有维度上全绿;单看一列,每个瓶颈都有多个方法在攻击。OLMo-core 3 的价值在于把多个正交轴(DDP + EP + PP + 分布式优化器 + rowwise 传输 + GPU 常驻路由 + recompute + MXFP8)叠在一起之后,各个方法的代价互相抵消了一部分。
重计算与通信重叠的代价
省内存的手段也要交税。per-block recompute(每块重计算)几乎省掉 2/3 的 activation memory、峰值显存降约 1/4,代价是约 1/5 的吞吐;通信与计算重叠(overlap)在部分测试里反而更慢,因为两个流抢同一份 GPU 资源。报告还记录了一种反直觉的失败模式 token gerrymandering:router 学会了「刷」负载均衡损失,使得负载均衡损失下降而真实负载不均反而上升,要诊断必须查请求的硬分区比例 f,而不是实际执行的路线。OLMo-core 3
容量税没有消失,只是被搬到别处
另一组研究者(Salesforce AI Research,2026 年 9 月)在长上下文方向独立测到了同一堵墙。120B / 241B / 667B 的 MoE 在 16 / 32 / 64 张 H200 上训练 1M context:调优过的 FSDP2 基线在 128K / 32K / 64K 就耗尽显存,而他们的组合栈把上下文推到 1M,相对基线达到 8–32× 的上下文可达性与最高 10.4× 的吞吐。MemPeak

图注:双 panel 对照图。左图是能跑的最大全局 batch(组合栈 1.5M / 1.8M / 3M tokens,相对调优 FSDP2 的 12× / 7× / 3×);右图是对数轴的 max context(组合栈在三个规模都到 1M,FSDP2 停在 128K / 32K / 64K,倍数 8× / 32× / 16×)。这是另一组研究者独立测得的容量张力证据。MemPeak
同一个月,「权重常驻、token 流动」在推理侧也长了出来。MoE-CORE(2026 年 10 月)处理的是显存放不下所有专家时的推理:prefill 阶段用两块交替缓冲暂存完整的 MoE 层,decode 阶段按路由历史为每一层维护最可能被命中的 expert 子集缓存,其余专家按需从主机搬运。MoE-CORE

图注:推理侧的横向流水线示意图。左侧 prefill 区用两块交替缓冲(B_0 / B_1)暂存完整的 MoE 层,layer l 算完释放给 layer l+2 使用;右侧 decode 区为每层维护 expert 子集缓存,配合路由历史替换、跨层预取与可选近似替代。注意这是推理侧工作,运行在 NPU 上,指标是 TPOT,与训练吞吐不可混比。MoE-CORE
训练侧的 DDP 常驻 + EP 搬运,与推理侧的常驻缓存 + offloading 协同,共享同一个判断:该常驻哪些权重、哪些走搬运,应该是显式可调的旋钮,而不是硬件的隐含约束。这说明「权重常驻」不是 OLMo-core 3 的孤立工程决定。
而最重要的一条收尾证据:这套栈不是只跑基准。Olmo 3 的 dense 7B 与 32B 模型就是用 OLMo-core 代码库训练的,真实训练吞吐为 7B 7700 tok/s/GPU(约 43% MFU)、32B 1960 tok/s/GPU(约 41% MFU)。Olmo 3
回到全篇主线:OLMo-core 3 把「容量 → 吞吐」这条曲线抹平了,但它证明的只到搬运与调度效率,够不到模型收敛质量,而且它只是更大的数据搬运问题里被解决的一环。稀疏化的 MoE 真正该省的地方从来不在 FLOPs,而在字节:把搬运单位从随总参数量增长的专家权重,换成随实际激活量增长的 token 行,再对剩下的每一次搬运斤斤计较。这才是「专家池越大、吞吐不再越慢」的全部秘密。
参考文献
本专题引用的核心资料如下,建议深入学习时阅读原文:
- Supercharging Olmo-core for Efficient and Scalable MoE Training (Ai2 Technical Report, 2026-10) — https://allenai.org/papers/olmocore3
- Olmo 3 (arXiv:2512.13961) — https://arxiv.org/abs/2512.13961
- Introducing Olmo-core 3: Open, scalable training infrastructure for large MoEs (Ai2 official blog via Hugging Face, 2026-10-01) — https://huggingface.co/blog/allenai/olmocore3
- DeepSeek-V3 Technical Report (arXiv:2412.19437) — https://arxiv.org/abs/2412.19437
- MoE Parallel Folding: Heterogeneous Parallelism Mappings for Efficient Large-Scale MoE Model Training (arXiv:2504.14960) — https://arxiv.org/abs/2504.14960
- PyTorch FSDP: Experiences on Scaling Fully Sharded Data Parallel (arXiv:2304.11277) — https://arxiv.org/abs/2304.11277
- DeepEP: an efficient expert-parallel communication library (deepseek-ai/DeepEP) — https://github.com/deepseek-ai/DeepEP
- Roofline: An Insightful Visual Performance Model for Multicore Architectures (CACM 52(4), 2009) — https://cacm.acm.org/research/roofline-an-insightful-visual-performance-model-for-multicore-architectures/
- MXFP8 (Microscaling FP8) — NVIDIA Transformer Engine Documentation — https://nvidia.github.io/TransformerEngine/features/low_precision_training/mxfp8/mxfp8.html
- GShard: Scaling Giant Models with Conditional Computation and Automatic Sharding (arXiv:2006.16668) — https://arxiv.org/abs/2006.16668
- Flattening Every Memory Peak in Long-Context Mixture-of-Experts Training (arXiv:2609.14306) — https://arxiv.org/abs/2609.14306
- MoE-CORE: Coordinated Expert Offloading and Residency for Memory-Constrained MoE Inference (arXiv:2610.01950) — https://arxiv.org/abs/2610.01950