AI 存储

https://github.com/chenzomi12/DeepLearningSystem/tree/main/06Foundation/03Storage

image-20231203164730851

image-20231203164910770

挑战

  • 海量小文件的高并发、低延迟读写

    海量多模态、异构小文件的高并发、低延迟读写。

    LLMs 以文本语料为主,同时存在pdf、 doc、excel 等各种格式,甚至存在医疗、气象等专用格式。大部分为半结构化的数据,其大小从 10KB 到 100 KB 不等小文本文件,存在数亿级的文件数量.。大模型训练过程中,频繁从数据集取 Token,每 Token 4 Byte,实时高并发小IO性能需要极低延迟存储性能瓶颈在于对小文件 OPS,而非带宽

  • 异构多模态数据,需分布式并行读写 (NLP不在其中)

    多模态数据快速训练加载,高度依赖分布式并行能力。

    • 异构多模态文件数据间存在关联、嵌套关系,如图-文对应、文-视频对应等
    • 异构数据需被高速加载到 LLMs 进行分布式训练,高并发、低延迟成为巨大挑战
  • 超大规模非结构化数据,需海量存储空间 AI 大模型需要高质量、大规模、多样性的数据集 随着数据和模型规模的增长,数据量会呈现指数级增长,独立存储无法满足应用需求。海量存储空间和可以横向扩展的存储系统尤为重要,分布式存储解决方案势在必行

  • 模型训练存储稳定性

    模型训练过程中,需要提供稳定的训练断点保存和回复的存储能力为了减少 TTA,高吞吐和低延时为 NPU 计算提供数据支撑,减少 NPU 计算等待模型训练 Checkpoint 必不可少,优化 Checkpoint 并缩短其耗时,减少训练中断时间存储模型 Checkpoint 时间,为 Checkpoint 数据可快速写入,需要高带宽提升存储稳定性,避免每次故障元数据同步,需要停止服务重新拉起

问题主要集中在训练和预处理流程。

指标

  • IOPS(Input / Output Operations per Second)

    每秒钟可以处理 I/O 数, 表示块存储处理读写。(输出/输入)的能力,单位为次,衡量存储系统 I/O 处理能力。

    指标 描述 数据访问方式
    总IOPS 每秒执行的I/O操作总次数 对硬盘存储位置的不连续访问和连续访问
    随机读IOPS 每秒执行的随机读I/O操作的平均次数 对硬盘存储位置的不连续访问
    随机写IOPS 每秒执行的随机写I/O操作的平均次数  
    顺序读IOPS 每秒执行的顺序读I/O操作的平均次数 对硬盘存储位置的连续访问
    顺序写IOPS 每秒执行的顺序写I/O操作的平均次数  
  • 吞吐量 Throughput

    • 吞吐量 = IOPS * I/O大小,单位时间内可以成功传输的数据数量,单位为MB/s,存储介质的 I/O越大,IOPS 越高,那么每秒 I/O 吞吐量就越高。
    • 当应用 I/O 大小较大,例如离线分析、数据仓库等应用,可以选择吞吐量更大的存储产品。如果部署大量顺序读写的应用,需要关注吞吐量。当应用 I/O 对时延较为敏感,比较随机且 I/O 大小相对较小,例如OLTP事务型数据库、企业级等应用,可以选择 IOPS 更高的ESSD云盘、SSD云盘。
  • 访问时延 Latency

    时延指块存储处理一个 I/O 所需时间,即发起 I/O 请求到 I/O 处理完成的时间间隔,单位为 s、ms 或者 μs。过高的时延会导致应用性能下降或报错。

    完成一个 IO 所花费时间(lat_io),对分布式存储系统来说,延时通常和如下几个因素有关:

    • lat_send:发送请求的延时
    • lat_recv:接收回复的延时
    • lat_srv_process:服务器处理请求的延时
    • lat_client_process: 客户端处理请求的延时
    • n: 一次 IO 需要请求的数量
    • 𝑙𝑎𝑡_𝑖𝑜 = (𝑙𝑎𝑡_𝑠𝑒𝑛𝑑 + 𝑙𝑎𝑡_𝑟𝑒𝑐𝑣 + 𝑙𝑎𝑡_𝑠𝑟𝑣_𝑝𝑟𝑜𝑐𝑒𝑠𝑠 + 𝑙𝑎𝑡_𝑐𝑙𝑖𝑒𝑛𝑡_𝑝𝑟𝑜𝑐𝑒𝑠𝑠) ∗ 𝑛

    此外,延迟还和如下因素有关:

    • 缓存及命中率:若IO请求缓存命中可以大幅减少IO延时。
    • IOsize:IO越大时延越大,但也不是绝对的,还和块对齐以及具体实现相关。
    • IOwait:IO等待时间,无论是网络IO还是磁盘IO或是存储实现,多采用队列保存请求,当队列长度 比较长时,就会有一部分时间是在队列中等待,进而影响 IO 延时。
  • 带宽 bandwidth

    以字节为单位衡量每秒钟的IO速率(bandwidth),即每秒钟可以处理的数据量,常以 MB/s 或GB/s 为单位,用于衡量存储系统的吞吐量。其通常与如下因素有关:

    • IO Size:每次IO的字节数量(io_size)
    • 并发数量:同时有多少并发请求(con_num)
    • IOPS:每秒钟完成的IO数量。iops = con_num * lat_io
    • 𝑏𝑎𝑛𝑑𝑤𝑖𝑑𝑡h = 𝑖𝑜_𝑠𝑖𝑧𝑒 ∗ 𝑖𝑜𝑝𝑠 = 𝑖𝑜_𝑠𝑖𝑧𝑒 ∗ 𝑐𝑜𝑛_𝑛𝑢𝑚 ∗ 𝑙𝑎𝑡_𝑖𝑜
    • io_size 越大,带宽越大,但实践中通常4k、8k、16k当IO较少时满足这种正比关系,当io_size达 到1M、4M、8M时系统吞吐量并不会继续增加。因为太大 IO实现也会分解为若干小的IO执行, 此外io_size增加可能导致lat_io增加,可能会互相抵消。

优化

训练数据存储

  • 大模型训练过程语料数据持续更新

  • 训练数据在不同数据预处理流程中频繁流动。有必 要针对非结构化和半结构化数据提供专用存储系统,例如数据湖

数据湖存储类型: 文件系统 对象存储对比

image-20231204094551555

  • 扩展能力:HDFS 采用集中式数据,规模受限。对象存储采用分布式数据可水平扩展,单集群可支持万亿文件、EB 级规模。
  • 存储成本:HDFS 诞生于存算一体的时代。对象存储天然面向存算分离设计,结合 EC 编码、分级存储等丰富的能力,可以实现大规模数据长期保存的更优成本。
  文件系统 对象存储
扩展性 NameNode 单点,层级命名空间 限制单集群上限10亿文件,PB级别容量 无架构扩展瓶颈 单集群可支持万亿规模文件,EB级别容量
成本 3X副本,存算一体架构,存储成本和计算成本同步增 长 EC 编码,1.5X 副本容灾 存算分离架构,存储成本个存储容量相关
可用性 可容忍2个副本损坏 取决于EC校验块数量,18+6可容忍6个块故障
吞吐 集群规模相关 和集群规模强相关
生态 传统大数据领域接受度高 存算分离架构流行,AI 领域接受度高

总结:

将公开数据集、训练数据、模型结果统一存储到对象存储中(数据湖),实现不同形态的数据 统一存储和高效流转。

训练流程

每轮 epoch 需要对数据集进行 Shuffle,然后将数据划分为 N batch,每次读取 1 batch 进行一次 训练迭代。周期性保存 Checkpoint 用于故障快速恢复。

分析

数据清洗过程

Shuffle 和Read数据读取对存储的操作:

  • Shuffle:元数据操作,主要是针对数据List

  • Read:元数据操作&数据本身的操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # trainging Epoch
    for each in epoch:
      # List files in the datasets
      # pre-processing of datasets
      for mbs in batch:
        # Read files of the batch
        # Training
        pass
    checkpoint.save()
      
    

对于大量小文件,延时和吞吐是性能关键

  • 文件越小,元数据操作耗时占比越大
  • 延时、吞吐是降低数据清洗过程关键

小文件对象存储

元数据性能影响:对象存储采用 key-value 方式维护元数据,很好解决 小文件规模扩展问题

操作 & 协议路径的影响:数据清洗 Shuffle 用到的 List 操作性能延时偏高,对象存储协议访问路径长,小文件频繁读取延时大

存储数据加载

训练执行与 IO 时序关系:每轮 Epoch 耗时由数据 Shuffle 时间、计算执行时间、数据读取等待。

image-20231204151715657

优化方案

多层数据缓存

提供多层缓存加速,分层次提供远端、近端的数据读取性能。即按需将热数据缓存到 GPU 内 存和 Local 本地盘中,利用数据本地性提供高性能访问。e.g.,训练先将 Checkpoint 写到性能相 对容易保证的本地存储,再向远端对象存储服务器(数据湖)上传。

image-20231204161435940

训练数据加载

尽量提高训练效率 and/or 利用率,减少 NPU 空闲,主要优化集中在

  • 优化数据清洗过程,数据搬运和处理与计算重叠;
  • 优化读取过程,让每 Epoch 读取数据耗时小于计算耗时,使得 I/O 时间被计算时间隐藏;
  • 优化 Checkpoint 保存和加载过程,缩短 Checkpoint 耗时,减少训练中断时间;image-20231204153836625

调度优化减少数据等待

将数据集作为抽象实体统一管理,让非竞争性算力资源流水线化,提升计算利用率

image-20231204160016029

同步的存储加速

与训练数据以小文件为主不同,大模型单个节点 checkpoint > 100GB。多个训练节点同时写, 需要恢复时又同时读,对存储提出了很高的吞吐要求。Checkpoint 保存和加载期间整个训练是中断的,因此提高吞吐,将 Checkpoint 耗时控制在尽量 小的占比,对于减少训练等待、提高计算资源利用率非常重要。

Checkpoint 直接写入 CPU 内存或 NVME SSD, 并采用流式 & 分块上传的方式,无需等 Checkp oint 全部写完就开始向数据湖上传

  • 异步写来突破上传对象存储的带宽限制。训练程序不用等待数据上传完成,即可恢复训练。其耗时取决于 NVME SS D 写入能力,极大缩短 Checkpoint 写入时间。
  • 对于 Latest Checkpoint 采用异步写的同时,让其驻留在 CPU 内存,当训练需要恢复时直接读取,解决 Checkpoint 快速加载问题。

image-20231204212441018

CKPT 保存流程

LLMs 中训练数据加载开销远小于整个训练过程开销,该优化收益有限(而且可 能影响用户使用习惯和降低易用性),因此很多存储优化方案专门针对Checkpoint。

频繁保存CKPT原因

大模型在训练过程中,可能会遇到硬件故障、系统问题、网络错误、液冷供给等问题。千卡AI 集群 2~3天,频繁中断会导致大模型训练难以丝滑持续进行,因此需要通过 Checkpoint(保存 模型权重参数) 来保存和恢复进度。

对于百/千亿参数 LLMs 大模型,Checkpoint 时间开销从分钟级到小时级不等(Checkpoint 存储 read/write 耗时与模型大小成正比) 。Save Checkpoint 时大模型训练任务需要暂停。

一旦大模型训练过程出现中断,之前迭代的 Epoch 次数在恢复时需要重新计算,通常会花费数 小时的时间。大模型训练过程一般使用 千卡规模 AI 集群,因此总体损失数千个 NPU 卡时间。

保存加载流程

  • 大模型训练过程频繁保存和加载 Checkpoint(CKPT)都是个灾难
  • Checkpoint 配置的频率,决定了 AI 集群中故障恢复后的重训成本

大模型因为模型参数量太大,需要切分到不同 NPU,每个 NPU 有独立的模型权重参数。

  • Save :保存一次完整Checkpoint需要聚合所有NPU的权重信息,然后再回传对象存储服务器。
  • Write:加载一次 Checkpoint 需要把聚合后的模型权重按照 AI 集群分布进行切分,逐个 NPU 加载。

image-20231204205506277

保存加载策略

什么时候应该完整保存 Checkpoint、什么时候直接保存单个 NPU Checkpoint

  • 数据湖保存分片的 CKPT 和完整的 CKPT
  • 程序定期保存分片 CKPT,定期汇聚分片 CKPT

加载时什么时候对完整 Checkpoint 进行切分后加载,什么时候直接加载

  • 如果没有节点坏掉,中断恢复直接拉起分片存储 的 CKPT,便于加载
  • 如果节点坏掉,使用完整的 CKPT 根据新的分布 式策略进行切分后重新拉起

文件成分分析

Checkpoint 主要存储是网络模型状态的元数据

  • 网络模型权重参数Modelparameters
  • 网络模型梯度参数Modelgradients
  • 优化器状态信息OptimizerStatus
  • 训练状态信息 T raining Status
  • 配置信息Configuredata

Checkpoint 用途:故障快速恢复、二次训练、模型微调

Checkpoint 保存加载:torch.save() and torch.load()

  • torch.save()

    网络模型在训练过程中会间隔一定时间保存一次 Checkpoint,用来记录当前训练的状态信息。

    save() 数据序列化过程主要步骤

    • pickler元数据序列化 data.pkl
      • 网络模型结构信息
      • 数据类型
      • 序列化后模型层的key
    • save存储数据 data.id_N
      • 实际存储的张量 Tensor 数据
  • torch.load()

    save() 的逆操作过程,数据根据 I/O 顺序读,不做过度的分析

    load() 数据反序列化过程主要步骤:

    • pickler元数据反序列化
    • read读取key对应的value数据

优化方案对比

优化 Checkpoint 保存和加载过程,缩短 Checkpoint 耗时,减少训练中断时间。

    成熟度
将CKP T模型数据存放在数据湖 save() and/or load() 5
CKP T的save过程从同步到异步 save() 2
CKP T流式分块存储 save() 2
多文件加速聚合 save() and/or load() 4
本地内存缓存,同步写内存 save() and/or load() 4
数据拷贝过程使用零拷贝 save() and/or load()  

将CKPT模型数据存放在数据湖

  • 单节点:每个训练节点使用更快的NVME SSD代替HDD进行存储,再分层回传数据湖;

  • 多节点:一个节点挂了,其CKPT被其他节点代替继续进行训练;
  • 优点:大容量、高吞吐、数据可共享

save() 过程由同步到异步

  • 将 save() 过程与下一轮的训练迭代并行,计算掩盖存储耗时。(系统崩溃、节点挂掉时需要处理数据部分写入逻辑。CKPT 添加 success 标记位便于恢复)

CKPT流式分块存储

采用流式 & 分块上传的方式,无需等 Checkpoint 全部写完到 Memory/SSD 就开始向数据湖上传。

系统崩溃、节点挂掉时需要处理数据部分写入逻辑。CKPT 添加 success 标记位便于恢复。

多文件加速聚合

torch.save() 将多个 tensor 分别写入各自文件 idx,对meta data要求高,写入开销大。

重写 torch.save()/load() 方法定义数据存储流程,聚合多个 tensor 数据文件;为了 load 过程中数据读取,保留每份数据的索引 index。

image-20231205091201243

把多个tensor文件聚合传输,避免系统对小文件快速读写,减少IO开销。

本地内存缓存,同步写内存

对于 Latest Checkpoint 采用异步写的同时,CKPT save() 时驻留在 CPU 内存,当训练需要恢复 时 load() 直接读取,再从数据湖中读取备份到内存,解决 Checkpoint 快速加载问题。

数据拷贝过程使用零拷贝

通过操作系统的内核技术,实现用户 buffer 间的数据传递,达到数据零拷贝、内存节省的目的。 此时 CKPT 数据要求存放在节点内存中。

存算

近存计算的好处

  • 减少数据在片内和片外的流动搬运,所带来的性能开销,减少时延
  • 降低能耗,是系统架构发展的本质要求所驱动。

存算分离的好处

  • 计算算力和存储能力能够根据自身业务动态 Scale,更容易扩展
  • 计算、存储和网络关键指标不同,计算重吞吐,存储重稳定性,网络重时延
  • 系统复杂度不同,存储对于系统复杂度容错率低,高可用度要求严苛
  • NVIDIA H100 本质上属于存算分离架构,逻辑上把节点的内存和显存结合一 起,通过 NVLink 高速访问。