乐趣区

关于人工智能:如何借助分布式存储-JuiceFS-加速-AI-模型训练

传统的机器学习模型,数据集比拟小,模型的算法也比较简单,应用单机存储,或者本地硬盘就足够了,像 JuiceFS 这样的分布式存储并不是必需品。

随着近几年深度学习的蓬勃发展,越来越多的团队开始遇到了单机存储的瓶颈,分布式存储在 AI 畛域的重要性一直凸显。AI 团队通常会面临以下几种问题:

数据集太大

随着数据量和模型规模的减少,单机存储往往无奈满足需要。为解决这些问题,就须要应用分布式存储。

历史数据集须要进行全量归档

在某些利用场景每天都会产生大量新的数据集,这些数据集在一段时间后将变为历史数据,须要进行归档。因为这些数据的生成老本较高,因而不能轻易删除,尤其是在主动驾驶畛域,如路测车采集的雷达和摄像头数据,这些数据对公司来说是极为贵重的资产。若要整顿这些数据,传统的单机存储显然不足以满足需要,因而须要思考应用分布式存储。

小文件和非结构化数据太多

针对传统的分布式文件系统,治理大量小文件会带来元数据存储的累赘,对于视觉类模型,影响更加显著。解决这一问题的办法是应用对小文件存储敌对的分布式存储系统,这样能够保障下层训练任务的高效率,同时也可能不便地治理大量的小文件。

训练框架须要 POSIX 接口

最后算法科学家在做模型调研时,都是基于本地的资源来做研发和数据的拜访,但当须要在分布式存储上进行更大规模的训练时,本来的代码个别不会做太多的调整。所以这就要求分布式存储须要反对 POSIX 接口,最大水平上兼容本地开发阶段的代码。

公共数据集须要不同团队共享,也可能须要数据隔离

在某些畛域,如计算机视觉,有一些权威的公共数据集,这些数据集须要在公司外部不同的团队间共享。为了不便团队之间的应用,通常会将这些数据整合并打包存储到一个共享存储中,防止不必要的数据复制和冗余。

云上训练的数据 I/O 效率不高

在云上进行模型训练通常是应用对象存储作为底层存储的存算拆散架构,因为对象存储的读写性能较差,在训练上会有很大的瓶颈。

本文将会介绍在模型训练中如何应用 JuiceFS,以及优化训练效率的实际。

1.JuiceFS 在模型训练场景中的架构

上图是架构图,分为三个局部:

第一局部:元数据引擎,依据集体抉择,能够应用任何数据库,例如 Redis、MySQL 等等,作为元数据引擎。

第二局部:底层数据存储,在云上或公有云中,应用对象存储服务来对接 JuiceFS。

第三局部:JuiceFS 客户端,用户在应用时须要在每个 GPU 和计算节点上挂载 JuiceFS,这样就能够像拜访本地硬盘一样拜访 JuiceFS 的文件系统。

底层存储依赖于对象存储中的原始数据,同时每个计算节点上还有一些本地缓存,包含元数据和数据缓存。JuiceFS 的设计中,每个计算节点的本地能够有多级缓存。第一级是基于内存的缓存,第二级是基于本地磁盘的缓存,只有在本地缓存没有命中时,才会拜访对象存储。

如果进行单机模型训练,在首轮训练时,训练集或数据集通常不会命中缓存。然而从第二轮开始,在缓存资源短缺的状况下,简直不须要拜访对象存储,达到减速数据 I/O 的成果。

JuiceFS 读写缓存流程

咱们之前进行了一项评测,比拟了在拜访对象存储时,应用缓存和不应用缓存这两种形式对于训练效率的影响。评测结果表明,这两种形式的性能差异十分大。(点击  此处 ,理解评测后果)

上图展现了 JuiceFS 缓存读写的流程。最下面是应用程序,它相当于是最后发动读申请的终点利用或训练任务。

当应用程序发动读申请后,申请会先进入左侧的内核空间,内核会查看内核页缓存中是否有申请的数据。如果内核页缓存中没有数据,申请会回到用户空间的 JuiceFS 过程。在用户空间,JuiceFS 过程会解决所有的读写申请。

JuiceFS 默认会在内存中保护一个读缓冲区,当申请未能从缓冲区中获取数据时,JuiceFS 会进入块缓存索引,即基于本地磁盘的缓存目录。JuiceFS 默认将文件切割成 4MB 的块并存储,因而缓存的粒度也是 4MB。

举个例子,当拜访一个文件的一部分数据时,JuiceFS 只会缓存该局部数据对应的 4MB 块到本地缓存目录中,而不会缓存整个文件。这是 JuiceFS 与其余文件系统或缓存零碎的显著差别之一。

Block cache index 用于疾速定位本地缓存目录中的文件块。如果找到了,JuiceFS 过程会主动读取本地盘,而后进入内核态,读取实现后返回给 JuiceFS 过程,最初返回给应用程序。

当本地盘数据读取实现后,数据还会进入内核页缓存。这是因为如果没有应用 direct I/O,Linux 零碎默认会将数据存储在内核页缓存中。这些内核页缓存都用于减速缓存拜访,如果第一个申请间接命中并返回,那么效率是最高的,并且申请不会通过 FUSE 层进入用户态过程。如果没有命中,则会通过 index 查找,如果在节点目录中没有找到 block,则会通过网络申请达到对象存储,而后将数据读回来并原路返回给应用程序。

从对象存储上下载数据时,JuiceFS 会有一个后盾异步线程,把读回来的 block 同时写到本地缓存盘里,确保下一次拜访同样的 block 时,能间接从本地缓存命中,而不须要再次从对象存储上获取。以上就是 JuiceFS 建设读缓存的流程。

上图有一部分的模块叫 Chunk Cache,chunk 是 JuiceFS 中的一个逻辑概念,每个文件会依照 64MB 大小分为多个 chunk,来晋升大文件的读取性能。这部分信息会被缓存到 JuiceFS 过程的内存里,来减速元数据拜访的效率。

与数据缓存不同,元数据缓存工夫较短,并且为确保强一致性,open 操作默认不缓存。思考到元数据流量很小,所以对整体的 I/O 性能影响比拟小,然而在大量小文件的场景,如果须要频繁拜访小文件,元数据的开销也会占到肯定的比重。

2. 为什么训练太慢以及如何排查?

当应用 JuiceFS 进行训练时,性能是最重要的思考因素,它间接影响到模型训练的速度。以下是可能影响 JuiceFS 效率的几个方面:

元数据引擎

在解决小文件时,抉择不同的元数据引擎(如 Redis、TiKV、MySQL)的性能差异很大。JuiceFS 官网提供了一份比照它们作为元数据引擎的性能文档,均匀来说 Redis 会比其余数据库快 3~5 倍。如果发现元数据申请特地慢,倡议尝试应用一些性能更好的数据库作为 JuiceFS 的元数据引擎。

对象存储

次要影响数据存储拜访的性能和吞吐量。如果在云上应用,通常应用私有云提供的对象存储服务,其性能绝对固定。如果应用自建的对象存储,例如应用开源的 Ceph 或 MinIO 组件,能够对组件进行调优以达到更好的性能和吞吐量。

本地磁盘

缓存目录存储的地位对整个读取性能影响很大。在缓存命中率高的状况下,缓存磁盘的 I/O 效率会间接影响整体 I/O 效率。因而须要留神存储类型、存储介质以及磁盘容量等因素,数据集的大小也会对训练效率产生影响。

网络带宽

在第一轮训练实现后,如果数据集不足以在本地齐全缓存,网络带宽或网络资源的耗费会影响整体数据拜访效率。在云上,不同机型的网卡带宽也有所不同,这也会对数据的访问速度和效率产生影响。

内存

内存的大小会间接影响内核页缓存的大小。当内存足够大时,残余的闲暇内存能够用作 JuiceFS 数据的缓存,进一步放慢数据的访问速度。然而,当残余的闲暇内存较少时,数据拜访须要通过本地磁盘获取,这会导致拜访开销变大。另外,内核态和用户态之间的切换会对性能造成影响,比方零碎调用的上下文切换开销等。

如何排查

JuiceFS 提供了许多工具和命令来帮忙用户更好地进行性能调优和诊断。在去年的 Office Hours 中,曾经对如何在 JuiceFS 中进行性能调优和诊断进行了全面介绍。如果感兴趣,能够在 B 站上观看视频回放。以下是其中几个办法的简要介绍:

工具 1:juicefs profile 命令

它能够通过剖析拜访日志来帮忙用户更好地优化性能。每个文件系统挂载之后都会有拜访日志,但拜访日志并不会实时保留,只有在查看拜访日志时才会显示进去。相比间接查看原始的拜访日志,juicefs profile 命令会进行信息的聚合和相似滑动窗口的数据统计,并依照响应工夫从高到低排序,帮忙用户优先关注响应工夫较慢的申请,进一步剖析申请与元数据引擎或对象存储的关系。

工具 2:juicefs stats 命令

它从更宏观的角度收集监控数据,并实时展现进去。它能够监控以后挂载点的 CPU 占用、内存占用、内存中的缓冲区占用、FUSE 读写申请、元数据申请以及对象存储的提早状况等。这些粗疏的监控指标能够不便用户查看和剖析以后模型训练的瓶颈或性能问题呈现的可能环节。

JuiceFS 还提供了更底层的信息剖析工具,包含 CPU profile 和 heap profile。CPU profile 能够剖析 JuiceFS 过程执行速度的瓶颈所在,实用于相熟源代码的用户。而 heap profile 则次要用于剖析内存占用状况,尤其是当 JuiceFS 过程占用大量内存时,须要应用 heap profile 来确定具体哪些函数或数据结构占用了较多内存。

3. 一些常见的优化策略

元数据缓存优化

元数据缓存的优化计划次要分为两类:

1)调整内核元数据缓存的超时工夫

能够应用 --attr-cache--entry-cache--dir-entry-cache 参数,这三个参数别离对应不同类型的元数据:attr 示意文件属性(如大小、批改工夫、拜访工夫等),entry 是 Linux 中的概念,示意文件和相干属性,dir-entry 示意目录和其中蕴含的文件。

这些参数别离管制着元数据缓存的超时工夫。为了保证数据拜访的一致性,这些参数的默认值只有一秒钟,但在模型训练的场景中,原始数据通常不会被批改,因而能够将这些参数的超时工夫设置得更长一些,比方几天到一周等。但须要留神的是,元数据缓存是无奈被动生效的,只能期待超时工夫到期。

2)优化 JuiceFS 客户端的用户态元数据缓存

默认状况下,在关上文件时会强制申请元数据引擎获取最新的文件属性,以保障强一致性。但因为模型训练的数据通常不会被批改,因而能够关上 --open-cache 参数,并设置一个超时工夫,以防止每次关上同一个文件都反复拜访元数据引擎。另外能够通过 --open-cache-limit 参数管制缓存的最大文件数,默认值是 10000,即最多缓存最近关上的 10000 个文件的元数据在内存中,能够依据数据集的文件个数进行适当调整。

数据缓存

JuiceFS 的数据缓存分为内核页缓存和本地数据缓存两种,其中内核页缓存无奈进行参数调优。因而,在计算节点上应该尽量保留足够的闲暇内存,以便 JuiceFS 可能充分利用。如果计算节点上的资源缓和,JuiceFS 就不会将数据缓存到内核中。

而本地数据缓存相对来说用户更加可控,能够依据具体场景调优缓存参数。首先,能够调整缓存的大小(--cache-size),默认值为 100G,对于大部分场景都足够了。然而对于占用空间特地大的数据集,须要适当调整缓存大小,否则 100G 的缓存空间可能很快被写满,导致 JuiceFS 无奈缓存更多数据。配合 –cache-size 参数一起应用的另一个参数是 --free-space-ratio,这个参数用于管制缓存盘的空间闲暇比例,默认值是 0.1,即最多应用 90% 的磁盘空间缓存数据。

JuiceFS 还反对同时应用多个缓存盘,举荐尽量应用所有可用的盘。数据会通过轮询的形式均匀分布到多个盘中,从而实现负载平衡,同时最大化利用多块盘的存储劣势。

缓存预热

为了进步训练效率,能够通过预热缓存来减速训练任务。JuiceFS 反对预热客户端中的元数据缓存和本地数据缓存,通过应用 juicefs warmup 命令能够将缓存提前预热到缓存盘,从而在训练任务开始时间接命中缓存,提高效率。

增大缓冲区大小

缓冲区的大小也会影响读取性能。默认状况下,缓冲区大小为 300MB,但在高吞吐的训练场景下,这可能不够用。能够依据训练节点的内存资源状况来调整缓冲区大小,一般来说,缓冲区越大读取性能越好,但也不要设置过大的值(特地是在限度了最大内存的容器环境中)。须要结合实际负载状况进行调优,找到一个绝对正当的缓冲区大小。能够参考后面介绍的 juicefs stats 命令实时观测缓冲区的使用量。

如有帮忙的话欢送关注咱们我的项目 Juicedata/JuiceFS 哟!(0ᴗ0✿)

退出移动版