参考:面向大模型的存储加速方案设计和实践-百度开发者中心 (baidu.com)
对于一个典型的训练来说,可能迭代多轮 epoch。在每个 epoch 内,首先需要对数据集进行随机打散,然后将打散后的数据划分为若干 batch,每读取一个 batch 的数据,进行一次训练迭代。同时会周期性保存 checkpoint 用于故障快速恢复。
每一轮 epoch 的耗时都是由数据 shuffle、数据读取等待、checkpoint 和真正的训练时间相加得到。因此为了尽量提高训练效率,减少 GPU 的空闲,我们的主要优化就集中在三个思路:
优化 shuffle 过程,尽量将耗时控制在较小比例;
优化读取过程,尽量让每一轮读取数据的耗时小于计算耗时,这样就能让 I/O 时间被计算时间完全隐藏掉;
优化 checkpoint 过程,尽量缩短 checkpoint 耗时,减少训练中断时间。
shuffle 是一个纯元数据操作的过程,主要依赖大量文件的 LIST,读取过程则元数据和数据操作都有。而对于大模型训练用到的 KB 级小文件而言,文件越小,元数据操作的耗时占比也就越高;I/O 越小,延时和 QPS 越超过吞吐成为主要矛盾。因此,对于小文件的 shuffle 和读取,延时和 QPS 是提升性能的关键。
对象存储面对小文件时有哪些影响性能的因素。
其一,元数据方面,对象存储采用分布式 KV 或 NewSQL 维护平坦目录元数据,很好地解决了小文件的规模扩展问题。虽然大部分操作性能很好,但恰恰 shuffle 用到的 LIST 操作性能在部分场景下不够理想,延时偏高;
其二,对象存储协议的整个处理路径较长,对于大文件吞吐为主的场景可以忽略,但对于小文件的频繁读取则会带来不可忽视的延时开销。
对象存储的高吞吐能力给小文件读取带来的帮助比较有限,而在延时上也并不具有优势。面对这样的问题,我们有哪些解决思路呢?
我们分为几个方向来总结:
让敌人变弱,也就是通过一些打包格式减少小文件元数据操作的占比。比如 TFRecord、HDF5 等格式已经被大量应用。如果存储能配合这些格式做一些优化,那么效率将得到进一步提升;
让自己变强。硬件上通过燃烧经费购买高性能硬件不需要过多解释。软件上则一般通过架构升级提升系统的扩展能力,缩短 I/O 路径,这里的典型方案就是并行文件系统;
尽可能近身攻击。让存储和计算靠得更近,这里的典型方案就是缓存系统。利用数据集只读的特点,将元数据和数据缓存到计算节点,或者靠近计算节点的地方,也能大幅提高数据的访问效率。
从整体来看,前面我们已经基于对象存储的云原生数据湖解决了海量数据的存储和流动问题。这里我们可以进一步基于并行文件系统或缓存系统的数据存储加速层来弥补对象存储的短板,满足大模型各环节的性能需求。
「数据湖 + 加速层」的思路
今天的加速层设计,通常会选择内置自动的数据流转功能。比如在加速层通过 bucket link 建立对象存储某一个 bucket 到加速层的关联,就能自动完成数据在两层之间的流动。这种方式有诸多优点:
同步速度快,各节点可以并发同步数据,大大加快数据流转效率;
同步策略可定制,可以按照场景需求进行增量同步、选择性同步;
能够与调度器集成,实现数据准备与资源调度的高效配合;
避免了人工方式需要处理各类异常,做垃圾回收的问题。
当您提到在加速层通过 bucket link 建立对象存储某一个 bucket 到加速层的关联时,我理解您希望将对象存储中的一个 bucket 与加速层关联起来,以通过加速层来访问该 bucket 中的数据。这样的设置可以提高数据的访问速度和性能。
要实现这一目的,您可以执行以下步骤:
- 首先,确保您已经在对象存储服务中创建了一个 bucket,并且该 bucket 中包含了您需要加速访问的数据。
- 接下来,找到您使用的加速层服务提供商(例如,CDN提供商),并创建一个加速域名(或加速域名空间)。此加速域名将用于访问与加速层关联的数据。
- 在加速层的配置中,找到"Bucket Link"(或类似的术语),然后选择"添加 Bucket"(或相应的操作)。
- 在添加 Bucket 的对话框中,提供您在对象存储中创建的 bucket 的名称或标识符。根据提供商的要求,您可能需要提供其他相关信息,例如对象存储的访问密钥(Access Key)和密钥(Secret Key)等。
- 完成配置后,保存设置。此时,您的 bucket 便与加速层成功关联。
一旦完成了上述步骤,您的 bucket 中的数据将可以通过加速层进行访问。您可以使用加速域名来访问这些数据,加速层将会从对象存储中拉取数据并返回给最终用户,从而提供更快的访问速度。
请注意,具体的操作步骤可能因您使用的对象存储服务提供商和加速层服务提供商而有所不同。因此,在实际操作时,请参考相应提供商的文档和指南,以确保正确地完成关联设置。
这里就是一个基于自动数据流转实现调度优化,减少训练过程数据读取等待的例子。当两个任务都需要先加载数据然后才能开始训练,通过训练平台的流水线化调度,在一个任务做训练的同时发起下一个任务所需数据的提前加载,就能大大提高计算资源的利用率。
第二个场景,我们接着来看训练中的 checkpoint 部分。
与训练数据以小文件为主不同,大模型单个节点的 checkpoint 通常就能达到几十上百 GB。而多个训练节点同时写,需要恢复时又同时读,对存储提出了很高的吞吐要求。同时一个关键的问题是 checkpoint 期间整个训练是中断的。因此通过提高吞吐,将 checkpoint 耗时控制在尽量小的占比,对于减少训练等待、提高计算资源利用率同样非常重要。
训练程序先将 checkpoint 写到性能相对容易保证的本地存储,然后再通过外围工具向远端对象存储上传。这里需要考虑一个问题,如果要保证每个 checkpoint 都成功写入,那么总耗时就等于写本地耗时加上传对象存储的耗时,总体等待时间较长。因此实际应用中也有延迟一个 checkpoint 异步上传的方案。但无论如何,都引入了额外的复杂度,需要外围工具来处理各种异常情况。
新的做法。训练程序直接将 checkpoint 写入加速层的 Memory 或 NVME SSD,加速层采用流式上传的方式,不必等待 checkpoint 数据全部写完就开始向对象存储上传。此外,加速层也会采用分块上传的办法,充分利用对象存储的后端并发能力。
其实对 checkpoint 场景稍加分析就能发现,我们并不一定需要同步方案,因为训练过程的 checkpoint 会周期不断地进行,而发生故障恢复不应该是常态。因此要突破上传对象存储的带宽限制,异步写是一种值得尝试的思路。
这个方案最大的变化,就是对 checkpoint 文件的 close 操作变成了异步,训练程序不用等待数据上传完成,即可恢复训练,剩下的工作全部交给加速层透明完成。此时耗时主要取决于加速层的 Memory 和 NVME SSD 写入能力,可以大大缩短 checkpoint 写入时间。
另外,这里还有一个相关的优化,就是对于最新的一个 checkpoint 采用异步写的同时,让它驻留在加速层的缓存内。当训练需要恢复的时候就能直接从加速层读取,解决了 checkpoint 的快速加载问题。
推理阶段的模型分发部署。大模型的部署通常是百 GB 模型文件的高并发重复读取。由于推理服务规模大,模型迭代更新频繁,我们需要从提高吞吐和缩短流程两个方面考虑如何优化。
首先来看过去常用的基于镜像分发的方案。
训练产出的模型首先需要落到临时存储,完成镜像的制作,包括数据打包、压缩等过程,然后再从临时存储写入持久化的镜像仓库。在推理部署时,再从镜像仓库并发拉取到各推理实例的本地存储,然后进行解压和数据校验。可以看到在这个方案下,吞吐主要取决于镜像仓库底层存储的能力,而流程上在镜像制作和镜像分发两个阶段都需要引入额外的开销。
首先从流程上看,训练产出的模型直接写入统一的数据湖存储。这时可以通过对象存储的事件通知机制,让加速层立即感知并提前把模型文件的元数据和数据预加载到靠近推理服务的缓存内。当启动模型部署时,就能直接从缓存读取到数据。
其次从吞吐上看,基于 Memory 或 NVME SSD 提供靠近推理服务的分布式缓存,提供了很强的读吞吐能力,并且多实例重复读取同一份模型数据时不需要消耗大量的对象存储 I/O 和带宽。
另外,加速层可以通过不同的策略满足不同规模的分发加速需求。比如对于几十实例以下的小规模部署,采用单副本即可将所有缓存盘的 I/O 能力均衡地利用起来,满足读性能要求,同时还能大幅节省缓存空间,保证更多模型数据的缓存命中率。而对于数百实例的部署规模,采用多副本即可提高加速层的读吞吐能力。到了数千实例的规模,还能进一步结合客户端的 P2P 加速满足性能需求。
「数据湖 + 加速层」能够同时解决最开始提出的两个挑战,为大模型全流程提供了统一的存储解决方案。
百度沧海·存储加速的解决方案以及具体实践
对象存储 BOS 及周边生态提供了云原生数据湖的完整方案,解决了海量数据的存储和流动,以及大模型各环节间的衔接问题。
RapidFS / PFS 提供了数据湖存储加速的能力,满足了大模型各环节内的存储性能需求。
同时 RapidFS / PFS 通过与 AI 框架和训练平台的配合完成资源调度优化,整体效率做到最优。
“Fluid”是英语单词,意为流体。在计算机科学和其他技术领域中,这个术语可以有多种含义和应用。比如,在云计算和大数据领域,"Fluid" 可能指的是一种用于优化数据流动和处理的技术或框架。它可以涉及到数据的动态调度、弹性扩展、流式处理等方面,以提高数据处理效率和资源利用率。
K8s + Fluid 是一个结合 Kubernetes(K8s)和 Fluid 技术的方案。在这个方案中,Kubernetes 提供了一个强大的容器编排平台,而 Fluid 则增强了 Kubernetes 对数据密集型应用的支持,特别是在云原生环境下。
Fluid 是 CNCF 的一个开源项目,它是 Kubernetes 原生的分布式数据集编排和加速引擎。主要目标是服务云原生场景下的数据密集型应用,如大数据应用、AI 应用等。Fluid 的核心功能是通过缓存加速引擎将底层存储系统的数据缓存到计算节点的内存或硬盘上,从而解决计算与存储分离架构中的 IO 效率问题。
结合 Kubernetes,Fluid 提供了缓存数据的调度能力。这意味着缓存被纳入 Kubernetes 的扩展资源,使得 Kubernetes 在进行任务调度时能够参考缓存进行调度策略的分配。
总的来说,K8s + Fluid 的组合提供了强大的容器编排能力,同时优化了数据密集型应用在云原生环境下的性能。这种组合特别适合大数据、AI 等需要高效数据处理的应用场景。