本文是博主读 Fast Cloud Storage for AI Jobs via Grouped I/O API with Transparent Read/Write Optimizations 后的一些主观感想。
Table of Contents
1 团子观点
这篇论文在存算分离的云架构中,针对 AI 任务的 I/O 特征,在计算节点和后端存储之间垫了一层分布式读写缓存,并暴露了一组简单的分组 I/O API 来驱动去重和负载均衡。
博主认为,这篇论文值得关注它背后体现的四个架构思考,也是我们构建大型分布式存储工程的架构 sense。
1.1 利用闲置的算力侧硬件做分布式读写缓存
在典型的存算分离集群中,计算节点和存储节点之间的网络(S-NIC,通常 100Gbps)是刚性物理瓶颈,想要更多带宽就得买更多的存储服务实例。但论文观察到一个关键的资源错位:AI 任务在做 I/O 的时候,节点间的高速计算网络(C-NIC,200Gbps 起)和宿主机的 CPU 内存通常都没打满。
AITURBO 直接征用了这部分闲置资源——CPU DRAM 当暂存区,计算网络做数据中转和广播。这是一种典型的空间换时间、用局部高速网络规避全局慢速网络的工程折中。
1.2 在后端存储和计算之间插入独立中间层
过去几年,AI 框架的开发者被存储的慢逼得没办法,在 Megatron、DeepSpeed 的应用层侵入式地写了大量内存检查点、去重和通信协调逻辑。Megatron 花了四分之一的代码量(2228 行)来优化检查点读写——这种越俎代庖不仅难维护,而且应用层根本看不见底层的物理网络拓扑,优化效果打了折扣。
这其实已经是一种启发了:
- 应用层红温了,觉得协调逻辑耦合进去很重,这些事情推训框架开发者得侵入到存储系统特性,搞存储系统的逻辑。
- 存储层红温了,因为做的相对通用存储,没办法给每个框架专门设计自己的架构。
这种两难的实际问题一般意味着:我们得加一层,让整个事情顺理成章,更加丝滑。
AITURBO 在计算和后端存储之间插入了一个独立的中间层(Job Controller + Staging Buffer),将 I/O 路由、负载均衡和去重逻辑下沉。这和大数据分析领域的架构演进是同一套逻辑:在计算引擎(Spark/Presto)和底层对象存储(S3)之间架 Alluxio 或 JuiceFS。计算层专注算法拓扑,后端专注数据持久化,中间层接管缓存调度与去重合并。关注点分离做好了,上下游的职责边界就清晰了。
1.3 显式 HINT 比黑盒缓存更有价值
传统通用存储系统为了通用性,把上层应用当黑盒,依赖 LRU/LFU 之类的启发式算法猜缓存命中。但分布式 AI 训练有一个极强的特征:拓扑确定性。一旦并行策略(DP/TP/PP)确定,数据的冗余映射关系在整个作业生命周期内保持静态——数据的值在变,但"谁和谁重复"这个结构关系不变。
这其实是一种对业务 IO 模型的洞察。
AITURBO 允许框架通过 HINT 告诉存储层"我的拓扑没变,直接复用上次的去重计划",从而绕过每次几百 GB 数据的哈希计算和路由求解。这种设计打破了存储系统的黑盒边界,但在工程上极其高效。比起在底层构建一套封闭的重度专用缓存系统,构建能与上层应用通过契约(HINT)做语义交互的透明中间层,可能更合理。
1.4 通用 vs 专用,永远需要权衡的天平两端
一个存储系统,我们考虑两个极端:
- 通用:意味着使用事实标准的协议和接口,势必要权衡照顾各个场景的应用。比如 fs 为了兼容 posix,势必会牺牲在简单无争抢场景下能达到的极限性能。
- 专用:意味着只能服务更少的客户,为少量用户设计专用架构和接口。反而,对用户来讲,专用即意味着供应商绑定。
作为存储架构设计者,我们永远需要根据实际需求去取这个合适的点。在文章场景下,使用 HINT 和中间层,可能是一个最优的选择。
2 论文做了什么
2.1 去重读写的前提:为什么 AI 任务有重复数据
AITURBO 的去重优化有一个明确的前提:AI 任务的分布式计算拓扑天然会产生冗余 I/O。
写入冗余的来源。 在分布式训练的数据并行(DP)模式下,所有 XPU 会持有完全相同的模型参数副本。这是 DP 的工作机制决定的:每轮迭代中,各 XPU 各自用不同的数据批次算出局部梯度,但在更新参数之前会执行一次 All-Reduce——把所有局部梯度汇聚成一个全局平均梯度,然后每个 XPU 用这个相同的梯度更新参数。起点相同,更新量相同,终点自然相同。于是写检查点的时候,所有 DP 副本写的模型参数是完全一样的。
这也解释了一个看似合理但实际不够的做法:既然大家写的一样,上层协调一个写者不就行了?问题在于,单节点的出站网卡带宽(比如 100Gbps)会成为死瓶颈。集群里明明有 8 台机器的 8 张存储网卡可以并行写,你只让一台写,另外 7 台闲着,带宽利用率只有八分之一。论文的 Figure 10 用 OpenSora 的实际代码说明了这个问题。
不同的并行配置下,重复的具体情况也不同:
| 配置 | 参数重复 | 优化器状态重复 |
|---|---|---|
| DP = 1(无数据并行) | 无 | 无 |
| DP > 1 | 有(重复因子 = DP) | 有(重复因子 = DP) |
| DP > 1 + ZeRO 1/2 | 有(重复因子 = DP) | 无 |
| DP > 1 + ZeRO 3 | 无 | 无 |
也就是说,当配置了 ZeRO 3 或纯 TP/PP 时,去重这把刷子就不起作用了。AITURBO 在这种场景下的收益主要来自内存缓冲和异步回写,而非去重。
读取冗余的来源。 推理服务在应对负载激增进行横向扩容时,大量新实例同时读取同一份模型权重;在 Agent 工作流中,并发子任务通常共享相同的系统提示词前缀,导致对同一 KVCache 块的重复读取请求。
2.2 基本 I/O 链路
AITURBO 通过暴露分组(Grouped)语义的 API 拦截 I/O 请求,将单文件点对点操作转化为全局协调的批处理。下面分别拆解写入和读取的物理执行过程。
分组写入
一次完整的分组写检查点,物理上分三步走:
第一步:哈希上报与去重检测。 各 XPU 利用硬件加速计算待写数据块的 BLAKE3 校验和(在 NVIDIA V100 上 1GB 文件只需 7.8ms,CPU 上是 35.6ms),连同文件元数据(如张量名称)上报给 Job Controller。Controller 通过比对校验和识别出哪些数据块是重复的。
第二步:数学规划与计划下发。 Controller 拿到去重后的数据块信息,求解一个双线性规划(Bilinear Programming)模型,目标是最小化端到端传输时间。模型的约束条件包括:每个源节点的出站带宽上限、每个目的节点的入站带宽上限、节点间的链路带宽限制、目的节点的内存容量限制,以及每个数据块必须且仅被传输 R 份(容错副本数)。
这个双线性规划问题本身是非凸非线性的,但作者发现所有二次项中的一个乘数都是同一个标量变量 $t$(总传输时间),而 $t$ 有明确的上下界。因此可以用分支定界法(Branch and Bound)迭代:固定 $t$ 后,问题退化为普通的线性可行性问题;根据是否有可行解,收紧 $t$ 的上界或下界。在 64 个 XPU、38B 模型的规模下,单线程 Python 求解器 4 秒出结果。
第三步:流水线执行。 XPU 按计划将去重数据经计算网络打散至各节点的 CPU DRAM(触发 future_0,对上层报告写缓冲完成),随后异步回写至后端云存储(触发 future_1)。两步之间用流水线重叠执行。
计划缓存。 由于 AI 训练的迭代性质,数据的冗余映射关系在并行策略不变的前提下保持静态。AITURBO 在第一次求解后缓存整个写入计划,后续迭代直接复用,甚至连哈希计算都可以通过框架提供的 HINT 跳过。
一个工程上的边界情况:训练最初几次迭代时,优化器状态全为零,算哈希会触发假性去重。系统需要等几轮迭代、数值更新后,去重模式才稳定下来。
分组读取
读取的去重比写入简单,通常直接通过文件名比对就够了。物理执行分两步:
- 系统仅安排部分节点从后端存储拉取单一数据副本到 CPU DRAM。
- 利用计算网络,通过串行转发(Serial Forwarding,复用 BlitzScale 的广播机制)将数据分发给请求组内的其余节点。两步之间同样用流水线重叠。
此外,分组 API 天然提供了一个分布式缓存的可能——如果某节点请求的数据恰好已被缓存在组内另一节点的 DRAM 中,系统可以直接走高速计算网络获取,完全绕过后端存储。论文测试中,命中缓存时在 64 个 XPU 上部署 72B(135GB)模型只需 2.25 秒。
2.3 亮点
API 抽象的层级提升。 将原本需要数千行应用层代码解决的去重和调度逻辑,抽象为只需修改几百行代码的底层 API 接口。Megatron 接入 AITURBO 后,I/O 优化代码从 2228 行降至 286 行,性能反超原生实现。Mooncake 的适配更少,只改了 44 行。
在存在重复数据的场景下收益显著。 在 1.5B 模型(DP=2,无 ZeRO)的配置下,AITURBO 比 Megatron + SFSTURBO 快 58 倍,比 Gemini 快 5.9 倍。去重 + 负载均衡的写入计划贡献了主要提速。
即使没有重复数据,内存缓冲仍然兜底。 在 38B 模型 + ZeRO(几乎无重复数据)的极端配置下,AITURBO 仍比直接写云存储快约 5 倍(23 秒 vs 111 秒)——收益来自将检查点先甩到 CPU DRAM,让 XPU 立刻回去算下一轮。
2.4 局限性
流量隔离粗放。 系统依赖硬件级 RoCE QoS 给存储 I/O 设最低优先级。面对 MoE 这种计算网络常态化高负载的架构,这种隔离方案大概率不够用。论文承认这一点,但将更高级的软件级流量整形留作了未来工作。
只适合大块数据传输。 控制器收集元数据和求解规划有固定开销(64 节点约 45ms),对小文件随机 I/O 没有收益甚至有害。
持久性语义让步。 写入 CPU DRAM 即向上层报告阶段性完成(future_0),牺牲了实时落盘的强持久性保证。节点宕机时数据可能丢失,依赖上层框架的检查点机制做回滚。这个权衡对训练检查点来说合理(丢了可以从上一个检查点重算),但不是所有场景都能接受。
缓存冷启动。 训练初期优化器状态全零引发假性去重,系统在前几轮无法启用计划缓存,必须等参数充分更新后才能稳定复用。
静态计划假设。 计划缓存依赖"拓扑不变"的假设。如果未来 KVCache 或中间激活状态的读写模式变得高度动态化,这种"一次求解、终身复用"的静态缓存机制就会失效。
3 小结
博主认为 AITURBO 是一个务实的领域专用中间件。它的价值不在于发明新存储,而在于敏锐地抓住了 AI 工作负载"存储慢、计算网络快、行为高度一致(分组)"的特征,用一个简单的 API 抽象实现了算存融合调度。
从更广的架构视角看,它再次印证了一个工程经验:当上下游的抽象边界导致性能瓶颈时,在中间垫一层、同时让上层通过 HINT 把确定性信息传递下来,往往比在任何一端硬做优化更有效。