在AI训练集群里,GPU算力越来越强,但数据系统未必能够跟上。
一张高端GPU可以在很短时间内完成一个训练batch的计算,但这个batch首先必须从存储系统读取出来,经过解码、解析、预处理和batch组装,然后通过CPU内存和PCIe等路径送入GPU显存。如果数据供应速度跟不上模型计算速度,GPU就会出现空闲等待。
因此,训练系统中的缓存并不是简单地“把常用数据放到SSD里”。缓存真正解决的问题,是缩短数据从存储系统到计算单元的路径,减少重复读取,并让慢速存储与高速计算之间形成更加稳定的数据流水线。
对于大模型训练、计算机视觉、语音识别以及推荐系统来说,缓存设计甚至可能决定昂贵GPU到底能跑多少有效训练吞吐。
为什么训练系统需要缓存
最直接的原因是存储系统和GPU之间存在巨大的性能差异。
训练数据可能存放在对象存储、分布式文件系统、NAS或者企业级存储阵列中。远程存储的容量可以非常大,但访问延迟和实际吞吐受到网络、并发、元数据以及文件布局影响。
如果每一个训练step都直接从远程存储读取数据,就会让GPU计算和存储I/O互相等待。
缓存可以在中间增加一个更高速的数据层。
例如原始训练数据长期保存在对象存储中,而训练节点使用本地NVMe SSD缓存近期需要的数据。第一次访问某个数据集时,从远程存储读取并写入本地缓存;后续训练任务可以直接从本地NVMe读取。
这样做的价值并不是让NVMe替代对象存储,而是减少相同数据被反复从远端读取的次数。
对于一个需要反复训练多个模型的数据集,这种收益尤其明显。
缓存首先应该解决I/O瓶颈,而不是盲目追求命中率
很多缓存系统喜欢强调cache hit rate,也就是缓存命中率。
这个指标当然重要,但它并不能单独说明训练性能。
假设一个训练系统缓存命中率达到95%,但剩余5%的缓存未命中数据每次都需要等待几百毫秒的远程读取,那么GPU仍然可能周期性出现明显停顿。
反过来,如果缓存命中率只有80%,但底层远程存储吞吐非常高,数据预取机制又做得很好,GPU仍然可能保持较高利用率。
因此AI训练缓存真正应该观察的是整个数据路径。
包括数据读取吞吐、读取延迟、cache hit rate、cache miss penalty、GPU idle time、DataLoader等待时间、CPU到GPU传输时间以及训练step time。
最终应该看的是训练吞吐,例如samples/sec或者tokens/sec,而不是单独看缓存命中率。
最常见的高速缓存其实是本地NVMe
对于GPU训练节点,本地NVMe SSD通常是非常实用的一层缓存。
假设一个训练节点需要从远程对象存储读取数TB数据,而多个epoch会重复访问相同的数据,那么可以把经常使用的数据集缓存到本地NVMe。
数据路径就会变成:
远程对象存储或分布式文件系统,经过网络进入训练节点,再进入本地NVMe,然后由CPU worker读取到系统内存,最后传输到GPU显存。
第二次访问时,数据不需要再次跨网络从远端读取,而是直接从本地NVMe进入系统内存。
对于大型GPU集群,这种设计可以显著降低共享存储系统压力。
但这里需要注意一个工程问题:本地NVMe缓存并不是永久数据存储。
GPU节点发生故障、实例被回收或者本地SSD出现故障以后,缓存可能消失。因此缓存中的数据必须能够从原始数据源重新生成。
如果企业把唯一的数据副本放进缓存,那就已经不是缓存,而是高风险的数据存储方案。
缓存的数据应该是什么
最适合缓存的数据通常具有几个特点:读取频繁、重新获取成本高、数据变化较少,而且可以安全地重新生成。
训练数据集就是非常典型的对象。
例如一个大型图像数据集可能有数TB,训练一个模型需要运行几十个epoch。如果每个epoch都从远程对象存储重新读取,那么网络和后端存储会承担大量重复读取。
如果训练节点有足够大的NVMe,可以把常用数据集缓存下来。
文本模型也一样。
如果训练数据已经完成清洗、tokenization和格式转换,那么将适合训练的数据缓存下来,可以避免每次启动训练都重新进行大量CPU预处理。
这也是为什么一些大型训练系统会把“数据准备”和“模型训练”分成两个阶段。
先把原始数据处理成高效的训练格式,然后训练节点直接消费这些数据。
不要把GPU权重和训练数据缓存混为一谈
原稿中提出“将高频访问的权重数据缓存到NVMe”,这个概念需要重新解释。
训练过程中,当前参与计算的模型参数需要在GPU显存中,因为矩阵计算直接发生在GPU上。
如果每一次模型计算都需要从NVMe读取权重,性能会完全无法接受。
因此NVMe通常不是GPU权重的正常工作缓存层。
在模型太大、无法完整放入单块GPU显存时,确实存在CPU内存、NVMe甚至其他层级的参数offload机制,但这属于显存不足情况下的内存层级管理,不应该被理解成普通的“热点数据缓存”。
一旦模型参数频繁从NVMe进入GPU,PCIe和SSD就会成为严重瓶颈。
因此对于训练系统,首先应该想办法让工作集尽可能驻留在GPU显存或者合适的高速内存层,而不是依赖NVMe持续供应每一次计算所需的权重。
Transformer的KV Cache也不是训练数据缓存
原稿把Transformer attention中的中间结果与SSD缓存联系起来,也需要纠正。
KV Cache主要出现在自回归推理场景中,用来保存已经计算过的Key和Value,从而避免生成每个新token时重新计算过去的上下文。
它和训练数据缓存属于完全不同的问题。
训练阶段通常需要进行完整的前向和反向计算,激活值、梯度和优化器状态有自己的内存管理机制。
如果把这些训练中间结果简单写入SSD,再在需要时读取回来,通常会产生巨大的I/O开销。
当然,现代训练系统存在activation checkpointing、CPU offload、NVMe offload等技术,用来在显存不足时降低显存压力,但它们属于内存优化和offload策略,不应该简单归类为“热点数据缓存”。
真正有效的缓存通常需要配合预取
单纯把数据放到NVMe并不一定能够让GPU保持满载。
更重要的是prefetch,也就是提前读取下一批数据。
例如GPU正在计算第100个batch时,CPU worker可以同时从NVMe读取第101个batch。
如果系统进一步支持异步数据传输,那么第101个batch还可以在GPU计算第100个batch的同时进入GPU可用的内存缓冲区。
这样,存储读取、CPU预处理、PCIe传输和GPU计算就能够重叠执行。
最终形成类似这样的流水线:
存储读取第N+2批数据,同时CPU处理第N+1批数据,GPU计算第N批数据。
只要各阶段速度匹配,GPU就不需要停下来等待数据。
因此,预取往往比简单扩大缓存容量更加重要。
一个容量很大的缓存,如果没有合理的预取机制,仍然可能导致GPU等待。
缓存容量不是越大越好
假设一个训练节点拥有8TB NVMe。
这并不意味着应该把所有8TB都作为缓存使用。
缓存本身需要占用空间管理、索引、元数据和后台淘汰操作。如果缓存数据和系统临时文件、checkpoint、日志等共享同一块SSD,还可能产生I/O竞争。
更重要的是,缓存容量必须与工作集匹配。
如果一个训练任务的数据集只有500GB,那么提供8TB缓存未必比1TB缓存产生更多收益。
反过来,如果一个训练数据集有50TB,而节点只有2TB缓存,那么就需要考虑哪些数据真正值得缓存,以及缓存替换策略如何设计。
缓存设计应该围绕working set,而不是围绕SSD容量。
哪些数据值得进入高速缓存
可以按照几个指标判断。
第一是访问频率。
如果同一个数据集会被多个epoch反复读取,那么它具有很高的缓存价值。
第二是重新读取成本。
从本地NVMe读取一个文件和从跨区域对象存储读取一个文件,成本完全不同。如果远程读取延迟高、带宽昂贵,那么缓存收益更大。
第三是数据稳定性。
很少变化的数据适合长期缓存,而不断变化的数据缓存命中率可能很低,还需要不断失效和更新。
第四是数据大小。
一个只有几KB的小配置文件没有必要占用昂贵的缓存设计资源,而大型训练数据集则可能非常值得缓存。
第五是共享程度。
如果同一份数据会被多个训练任务重复使用,那么集中式缓存或者节点级缓存的价值可能更高。
多节点训练不能简单地说“用NVLink共享缓存”
在分布式训练环境中,缓存架构更加复杂。
一个GPU训练集群可能有几十、几百甚至上千个训练节点。
如果每个节点都从对象存储读取相同的数据,那么后端存储系统会承受巨大的并发读取压力。
一种简单方案是每个训练节点建立本地NVMe缓存。
另一种方案则可以建立共享缓存层,让多个训练节点共同访问经过缓存的数据。
对于更大型的集群,还可以根据训练拓扑设计分层缓存。
最底层是GPU显存,再往上是CPU内存,然后是本地NVMe,再往上是节点级或者集群级高速缓存,最后才是远程分布式文件系统或对象存储。
不同层级的延迟、带宽、容量和成本完全不同。
但这里同样需要避免一个常见误区:NVLink并不是用来连接普通服务器之间缓存数据的通用网络。
NVLink主要解决特定GPU系统内部的高速互联问题。服务器之间的数据传输通常依赖高速网络,例如InfiniBand或者高速以太网,并可以利用RDMA降低通信开销。
缓存系统应该根据实际网络拓扑进行设计,而不是把NVLink当成万能高速缓存网络。
数据格式本身可能比缓存更重要
有时候企业花大量精力设计缓存,却忽略了训练数据格式。
例如一个训练任务需要读取几百万个非常小的JPEG文件。
即使SSD速度很快,大量小文件也会产生元数据访问、文件打开关闭、目录查找等额外开销。
如果把这些数据重新组织成适合顺序读取的大文件或者训练专用数据格式,性能可能比单纯增加SSD容量更加明显。
因此训练数据优化通常应该同时考虑:
数据格式、文件大小、数据布局、压缩方式、分片方式、缓存位置以及预取策略。
压缩也需要谨慎。
压缩可以减少网络和存储带宽,但解压本身需要CPU计算。
如果CPU已经是瓶颈,过度压缩可能导致CPU处理不过来;如果网络带宽才是瓶颈,适当压缩反而可能显著提高整体吞吐。
所以压缩率不是越高越好,而应该根据CPU、网络和存储之间的瓶颈进行平衡。
高速缓存什么时候值得投入
可以用一个非常实际的判断方法。
如果GPU经常等待数据,同时远程存储或者网络已经成为明显瓶颈,而且训练数据会被反复读取,那么增加高速缓存通常值得考虑。
如果GPU利用率很高,数据加载线程也没有明显等待,远程存储带宽充足,那么增加NVMe缓存可能几乎没有收益。
如果训练数据每次只读取一次,而且数据量远远超过缓存容量,那么缓存收益也可能非常有限。
例如一次性的ETL任务,把100TB数据顺序扫描一遍,节点只有2TB缓存,那么把大量时间花在缓存预热和淘汰上可能没有意义。
缓存最适合重复访问,而不是所有数据访问。
如何判断缓存是否真的有效
不要只看SSD读写速度。
真正应该做的是对训练过程进行端到端profile。
可以观察GPU利用率、GPU idle time、每个step耗时、DataLoader等待时间、CPU worker处理时间、存储吞吐、存储延迟、网络吞吐以及缓存命中率。
例如原来训练一个batch需要500ms,其中GPU计算350ms,数据加载150ms。
如果增加本地NVMe缓存以后,数据加载下降到30ms,那么step时间理论上可以明显下降。
但如果原来GPU计算已经占490ms,数据加载只有10ms,那么即使缓存把数据加载降到1ms,整体训练速度也几乎不会发生变化。
这就是为什么不能看到“NVMe比网络存储快很多”就直接得出“增加NVMe一定提高训练速度”的结论。
系统优化看的是关键路径,而不是某个组件的峰值性能。
缓存系统还必须考虑一致性和失效
训练数据发生变化以后,旧缓存不能永久存在。
如果数据集版本更新,而训练节点仍然读取旧缓存,就可能出现训练结果和数据版本不一致的问题。
因此成熟的训练平台通常不会简单地使用“文件名相同就认为是同一份数据”的缓存策略,而会结合dataset version、manifest、对象版本或者内容哈希进行管理。
对于不可变的数据集,问题相对简单。
例如Dataset v2026-08-31一旦生成,就不再修改。缓存只需要按照版本管理即可。
对于不断变化的实时数据,则需要更加复杂的TTL、版本和失效策略。
这也是为什么训练数据最好采用版本化管理,而不是直接修改线上数据文件。
缓存的终极目标是隐藏慢速存储
AI训练缓存的本质,可以理解成一句非常简单的话:让GPU尽量感觉不到底层存储有多慢。
但做到这一点并不意味着必须购买最大的NVMe阵列。
真正成熟的架构通常是分层的。
原始数据可以长期存储在对象存储或者分布式文件系统;经过处理、频繁使用的数据进入训练节点本地NVMe;当前正在处理的数据进入系统内存;即将计算的数据通过预取进入更适合GPU访问的内存区域;最终参与计算的数据进入GPU显存。
每一层负责解决不同的问题。
远程存储负责容量和持久性,本地NVMe负责高吞吐缓存,CPU内存负责预处理和数据缓冲,GPU显存负责当前计算工作集。
如果训练过程中发现GPU经常等待,就应该沿着这条数据路径逐层排查,而不是第一反应就增加SSD。
对于AI训练集群来说,最昂贵的资源往往是GPU时间。一个GPU因为数据I/O停在那里等待几十毫秒,看起来只是一个很小的延迟,但当数百张GPU连续训练数天以后,这些等待会累计成巨大的计算资源浪费。
因此,高速缓存真正的价值不是让某个SSD跑出更高的GB/s,而是让昂贵的GPU持续获得数据,让存储、网络、CPU和GPU形成稳定的流水线。
当缓存设计能够减少远程读取、隐藏I/O延迟、提高数据预取效率,并且实际提升samples/sec或tokens/sec时,它才算真正发挥了作用。否则,堆更多NVMe、增加更大的缓存容量,只是在服务器里增加了一层昂贵的“看起来很快”的存储。
AI 训练中的数据缓存有什么作用,什么时候应该把热点数据放到高速存储
图片说明:示意图 图片来源:Public Domain(公有领域)
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP