ARM 架构 JuiceFS 性能优化:基于 MLPerf 的实践与调优 随着国产芯片与 ARM 生态的快速发展如何在 ARM 平台上构建高性能存储基础设施成为技术焦点。Linaro 是一个专注于 Arm 生态和开源软件的国际化技术组织联合产业链上下游厂商解决共性问题并协助企业客户在开源基础上完成产品化落地。Linaro 团队在 MLPerf Storage 测试中对 JuiceFS 社区版元数据采用 Redis进行了系统压测覆盖多种典型机器学习训练负载。测试结果显示系统性能在很大程度上受内存带宽和元数据访问效率影响JuiceFS 的吞吐能力直接决定 GPU 利用率及训练效率。通过 UNet3D、ResNet-50 和 CosmoFlow 等负载的测试分析发现单机场景下GPU 利用率主要受内存拷贝延迟限制在双机或多机场景下元数据访问和节点间同步成为主要瓶颈。文章同时提供了针对这些瓶颈的调优思路与实践结果。总体来看大规模 AI 训练性能调优是一个系统工程需要从存储系统、内存带宽、CPU 调度、缓存策略等多方面协同优化才能在 ARM 平台上实现高效的深度学习训练数据供给。01 Arm64 与 x86_64 架构差异与并发特性概述相比 x86Arm 的应用范围不断扩大已从移动端延伸至 IoT、可穿戴设备、PC、汽车和服务器其高能效比performance per watt是广泛采用的关键原因。从架构设计上看Arm 属于 RISCReduced Instruction Set Computer精简指令集计算机x86 属于 CISCComplex Instruction Set Computer复杂指令集计算机。这种设计差异也影响了处理器的执行方式。Arm64 的指令长度固定为 4 字节而 x86 指令长度可变大约在 1–15 字节 之间因此 x86 往往需要更复杂的译码器。相比之下Arm 的指令更简洁也更依赖编译器和代码生成阶段对指令的有效组织所以需要更长的编译时间。从工程师可感知的角度来看还有一些架构差异会直接影响程序行为。很多在 x86 上看起来符合直觉的代码在 Arm 上未必如此后面要讲到的几个容易踩坑的点基本都与这些底层差异有关。其中一个典型问题是原子操作对地址对齐有要求。无论是 LL/SCLoad-Link/Store-Conditional还是 LSELarge System Extensions在执行原子加减等读改写操作时通常都要求访问地址满足对齐条件。较新的 LSE2 对这一限制有所放宽开始支持 16-byte window 内的非对齐访问。数据对齐对于 x86 来说不是必须的但保持良好对齐有助于提升性能。参考文档: Arm Architecture Reference Manual for A-profile architecture另一个需要重点关注的特点是Arm 采用的是弱内存序模型weakly ordered / relaxed memory model差别体现在对内存访问顺序的约束强弱不同。在多线程场景下同样的读写操作在 x86 上通常更容易表现为接近程序书写顺序而在 Arm 上则允许更多重排序因此其他线程观察到的读写顺序可能与源码顺序不一致。在 Arm 上出现的异常需要特别考虑内存序的影响。更多细节可参考 Arm 白皮书Synchronization Overview and Case Study on Arm Architecture。02 JuiceFS 与 MLPerf 概述JuiceFS 是一款开源高性能分布式文件系统基于对象存储构建在充分利用对象存储低成本优势的同时提供接近传统文件系统的使用体验。它支持 POSIX、HDFS SDK、Python SDK 以及 S3 兼容接口并能适配不同类型的应用和数据处理框架同时支持云原生扩展、数据安全与压缩可广泛应用于 AI 训练、推理、大数据处理等场景。为评估 JuiceFS 在 AI 训练等高负载场景下的数据供给能力可采用 MLPerf Storage 基准测试。该测试由 MLCommons 开发重点衡量存储系统能否持续高效向计算侧提供数据。2.0 版本将测试分为训练负载和检查点负载两类其中训练负载包括 3D U-Net、ResNet-50 和 CosmoFlow三者在样本大小和访问特征上存在显著差异并设置了最低 GPU 利用率要求3D U-Net 与 ResNet-50 为 90%CosmoFlow 为 70%。在测试流程中数据首先从存储系统读入主机内存再进入计算阶段。训练耗时由模拟方式复现以模拟真实训练场景的数据流从而无需实际部署 GPU降低实验门槛并提升操作便利性。03 MLPerf Storage v2.0 测试原理与调优在介绍具体的模型测试结果之前需要先了解分布式训练的数据访问原理有助于读者明白 GPU 利用率、存储吞吐和性能瓶颈的成因从而更好地理解后续的测试结果和调优策略。分布式机器学习通常采用数据并行方式即多个并行进程共享同一数据集每个进程分别负责读取和处理对应的训练批次。MLPerf Storage 的训练测试也遵循这一思路每个训练进程按批次从存储系统读取数据并通过模拟计算来评估存储系统的持续供数能力。为了理解测试中性能表现的来源还需要理解 JuiceFS 客户端的数据处理链路。使用 JuiceFS 进行测试时如图所示其执行流程大致可分为三部分。左侧应用侧 I/O 线程例如fio或 MLPerf Storage 的DataLoader线程负责发起read/write请求并等待请求完成。中间FUSE 守护进程中的请求处理主goroutine负责接收并处理来自内核态的 FUSE 请求将文件数据放入内存缓冲区和缓存并触发后端元数据与对象存储访问。右侧Meta client 和 ObjectStore client 的异步goroutine负责与后端 MetaDB 和 ObjectStore 集群进行数据与元数据交互。从性能分析的角度看这条链路需要重点关注两类问题。第一类是数据拷贝对应图中的 2.1、3、4、5、6 等步骤这些位置都会带来额外的内存复制开销因此往往是分析延迟和 CPU 开销时的重点。第二类是同步与异步边界。从图中可以看出1、2、3、4、5、6 这些步骤总体属于同步路径也就是请求发起后需要等待当前阶段完成才能继续向下推进而 7 属于异步路径由后台goroutine负责与后端存储交互。测试 1Unet 3d在这个测试中样本为 146 MiB 的图像文件我们主要关注大块数据的读取性能。测试结果显示单机环境下最高可稳定运行 5 块 GPUGPU 利用率约为 50%而双机场景可支持 10 块 GPUGPU 利用率同样约为 50%。为了提升数据读取效率对训练参数进行了优化将reader并发线程数从 4 调整为 16以加快数据生成速度并将数据读取方式改为 direct I/O以减少缓冲区和内存拷贝开销。业务指标显示当单机挂载 6 块 GPU 时GPU 利用率仅为 83%对应带宽约为 15.1 GB/s未达到预期的高利用率目标。进一步使用 FIO 对存储侧进行测试后发现其带宽同样约为 15.1 GB/s说明此时系统瓶颈已经落在 JuiceFS 客户端带宽 上而不是 GPU 计算侧本身。优化分析 1绑定 CPU为了深入分析客户端带宽受限的原因我们对进程进行了 CPU 绑定将其固定在 CPU1NUMA 2、3 节点运行。通过工具观察48 个 CPU 核心几乎全部占满进一步分析 top-down、memory 和 miss 指标发现系统表现出明显 Memory Bound主要耗时集中在内存拷贝上。这说明在 CPU 绑定场景下JuiceFS 性能瓶颈主要来自 CPU 处理能力和跨 NUMA 节点内存拷贝带来的额外延迟。优化分析 2 不进行 CPU 绑定为了理解系统在更通用条件下的带宽限制我们进一步观察了不绑定 CPU 的情况。通过观察可以看到CPU 并未被完全用满但 devkit tuner numafast 指标显示系统中的 remote 内存访问比例高达约 80%。这意味着大量内存访问已经跨越本地 NUMA 节点甚至可能跨越 CPU socket从而引入了显著的带宽损失和访问时延。从硬件带宽特性看跨片内存访问本身就存在明显限制。例如在 Arm 平台上跨 socket 的理论物理带宽约为 60 GB/s进一步通过实测跨片 copy 带宽在 Arm1 上约为 48 GB/s而在两组 x86 平台上分别约为 37 GB/s 和 28 GB/s。这说明在不绑定 CPU 的情况下虽然计算核表面上没有被完全耗尽但大量跨节点、跨 socket 的远端内存访问已经成为新的主要开销来源。因此可以推测此时 JuiceFS 带宽无法继续提升很可能并不是单纯受限于 CPU 算力而是受限于跨片内存访问的带宽与时延。换言之系统瓶颈已经从“本地 CPU 忙不过来”转移为“远端内存访问代价过高”。综合来看在两种场景下JuiceFS 带宽无法提升的原因并不相同绑定 CPU 时主要受限于 CPU 资源消耗以及大量内存拷贝带来的访问开销不绑定 CPU 时主要受限于高比例 非本地内存访问尤其可能是跨 socket 访问带来的带宽和时延损失。测试 2Resnet50ResNet-50 测试的单个样本较小单个样本大小约为 150 KiB每个 batch 包含 400 个样本每个 batch 的总数据量约 58.5 MiB。本次 I/O 测试关注 GPU 高并发下的数据加载效率及训练吞吐。测试显示系统可在大规模 GPU 下维持较高利用率单机50 块 GPUGPU 利用率 95%带宽约 9.2 GB/s双机96 块 GPUGPU 利用率 90%带宽约 16.9 GB/s在测试过程中我们将关键参数reader.read_threads从 8 调整为 1对于该模型中等大小的图像模型单线程即可满足数据供给需求。优化分析 1 单机性能瓶颈与内存带宽影响在单机配置 55 块 GPU 时GPU 利用率下降至 86%带宽仍为 9.2 GB/s表明系统瓶颈已转移至 JuiceFS 客户端带宽。进一步分析发现ResNet-50 测试采用 Buffer I/O 模式除了读取数据外处理数据集时的 内存拷贝会消耗一部分内存带宽。系统内存拷贝带宽受内存通道数、内存频率及 CPU 频率影响。通过对多台配置不同的机器进行 stream 测试得到的单机顺序读带宽与系统内存带宽测得的可比带宽一致表明读数据吞吐能力在很大程度上取决于系统内存带宽。对于需要高吞吐、高 GPU 利用率的训练任务建议优先选择内存带宽较高的机型可显著提升数据供给能力和训练效率。单 CPU 内存拷贝带宽数据JuiceFS 单机部署读带宽Arm3Arm3: 171 GB/s25.3 GiB/sArm2114 GB/s21.6 GiB/sArm1106 GB/s18.3 GiB/sx86290 GB/s17.9 GiB/sx86182 GB/s16.6 GiB/s优化分析 2双机扩展瓶颈与分布式限制在多节点部署中除了单机性能限制外跨节点内存访问、网络传输和元数据延迟会成为新的瓶颈因此在单机分析之后进行双机测试有助于识别这些分布式约束并指导系统优化。在双机场景下理论上可以支持 100 块 GPU但实际测试中只能达到 96 块 GPU。通过分析发现每个操作的读取延迟有所增加。尽管文件数据已缓存在本地盘上元数据访问延迟仍然成为主要限制因素。为解决这一问题对系统进行了多方面优化将 CPU 核心分组保证训练线程与 I/O 线程在同一 NUMA 节点上运行。将纯数据处理和元数据访问分别分配到不同 CPU 核心和存储路径上。调整 Redis 缓存和本地缓存策略减少高并发访问元数据时的延迟。经过上述调优后双机场景能够稳定支撑 100 块 GPU 运行GPU 利用率达到预期水平。测试 3cosmoflow与之前的模型相比这个模型的单样本数据量更小这也意味着对 I/O 和元数据访问的要求更高。在单机和双机场景下CosmoFlow 测试显示单机最高稳定 10 块 GPU偶尔可撑到 12 块 GPUGPU 利用率约 75%带宽约 5.6 GB/s关键参数调整将reader.read_threads从 4 调整为 1每次读取 batch 数据量为 2 MiB单线程即可满足数据供给需求。优化分析 1单机瓶颈内存拷贝限制 GPU 利用率当尝试增加 GPU 数量超过 10 块时发现 GPU 利用率下降。分析日志和性能数据后发现数据读取时间增加而元数据访问延迟变化不大。文件数据已缓存在本地盘上磁盘队列未满延迟也不高因此瓶颈不在存储设备。使用性能分析工具观察发现关键瓶颈操作主要集中在 内存拷贝memcopy数据读取流程中多次拷贝操作的延迟累积导致整体读取时间增加。由此推测当系统使用更多内存带宽时内存拷贝延迟成为限制读取性能和 GPU 利用率的主要因素。优化分析 2双机瓶颈分布式同步与元数据延迟在双机场景下尝试 20 块 GPU 时第一轮测试 GPU 利用率明显偏低。进一步分析发现一台机器已开始训练而另一台机器仍在进行 Dataset 预处理包括读取文件列表和分片操作。由于 CosmoFlow 数据量较大高索引文件的读取耗时较长导致两台机器未能同步开始训练第一轮 GPU 利用率下降。为解决该问题在代码中加入同步机制确保所有节点在开始训练前完成 Dataset 预处理。经过该调整后双机测试能够稳定支撑 20 块 GPUGPU 利用率达到预期水平。04 总结首先MLPerf Storage 通过不同样本、文件和 batch 大小的组合考察文件系统的各项能力包括大中小块顺序读能力、文件并发性能、总读带宽、元数据访问时延、文件读取时延以及文件操作的稳定性。在只读文件场景下充分利用高速近端缓存包括原数据和元数据缓存可以显著提升读取性能。需要注意文件越小对 IOPS 和延迟的要求越高。其次我们发现系统的内存和带宽对性能影响显著。在 Memory copy 密集型的应用中内存拷贝不仅消耗内存带宽同时也占用 CPU表面上会出现“CPU 忙”的假象实际上 CPU 大部分时间是在等待数据。测试结果显示系统内存带宽对 JuiceFS 的吞吐能力有决定性影响这也为选择服务器提供了参考标准内存带宽越高的系统其存储吞吐性能也越好。第三Go 运行时对 NUMA 的感知有限对于大规模 CPU 核心运行场景性能可能不如小规模核心运行。对于多 NUMA 系统应尽量避免跨 NUMA尤其是跨 CPU socket 的访问因为跨 socket 内存带宽通常较低约几十 GB/s会增加延迟并影响整体性能。因此实际部署时只需要分配足够的 CPU 核心即可无需过度使用全部核心以避免额外的内存访问延迟。第四在系统层面还有一些潜在优化点。例如对于 Memory copy 密集的操作部分 Arm 新系统提供了针对内存访问的指令优化我们与 Arm 社区合作将配置提升推送到社区中新系统可以显著提升内存拷贝效率在部分场景中带宽提升可达数十个百分点。此外对于涉及大量内核与用户态交互的操作例如文件读写和元数据处理可以通过优化用户态与内核态的交互减少不必要的调用次数从而降低延迟。实践中也发现将文件处理尽量集中在同一生产节点内避免跨 NUMA 或跨 socket 的访问可以进一步提升性能和稳定性。