尽管当前 GPU 备受瞩目,但对于 AI 基础设施来说,CPU 仍不可或缺。它犹如大脑般负责服务器的整体运算与控制,协调内存、数据流、I/O 等,确保系统稳定运行与高效协作。即使面对计算密集型任务,CPU 也扮演着保障任务顺利执行的重要角色。
然而,大部分应用程序默认使用 BIO(Buffer I/O)访问数据,这种方式在 AI 训练过程中,会出现存储服务模块 CPU 使用率过高的问题,从而限制 GPU 的并行处理能力,影响系统稳定性,拖慢整体训练速度。本文将深入解析并分享焱融科技在存储实践中探索出的创新混合 I/O 解决方案。
BIO 和 DIO
Linux 和其他大多数操作系统均支持 Buffer I/O(简称BIO)和 Direct I/O(简称 DIO) 两种文件访问模式。在 BIO 模式下,读或写文件时,虚拟文件系统(VFS)会将文件数据缓存到 page cache 中。此机制是默认的文件访问模式,易于与应用程序集成。它无需特别处理 I/O 大小和对齐约束问题,且性能相对较好。
BIO 的显著优势在于其缓存策略,在多次访问相同数据时,能够通过缓存命中提高性能,并通过 readahead 预取数据及 writeback caching 方式聚合小写入操作,尤其在顺序读写小 IO 或追加写场景中,其效能尤为突出。
相比之下,DIO 模式则采用了更为直接的数据传输路径,直接在应用程序与存储设备间传输数据。然而,DIO 模式虽然省去了数据复制的步骤,但数据必须满足特定的大小和对齐约束要求。
BIO 引发内存管理开销增加
在大部分应用程序中,包括 AI 训练,通常默认采用 BIO 模式进行数据访问。然而,在 AI 训练的日常应用中,我们遇到了存储服务模块 CPU 使用率异常高的问题。在全闪存环境中,通过使用 top 或 sar 等工具查看 CPU 使用情况,我们发现 system 占用率非常高,同时大部分内存被 buff/cache 占用。
这引发了我们的推测:文件缓存的换入换出操作导致了 CPU 资源的过度消耗,即大量的 BIO 操作使得 CPU 使用率飙升。这一现象揭示了 Buffer I/O 的一个主要缺点,即增加内存管理的开销。BIO 使用的页面缓存会在用户空间和内核的页面缓存之间产生额外的数据副本。对这些缓存页面的管理会导致多方面的开销,包括伙伴系统的申请和释放,以及 LRU 链表的扫描、老化和回收等。具体如下:
在短时间只对数据进行一次读取或写入操作的场景下,BIO 不仅增加了内存拷贝的成本,还增加了申请和释放缓存页面的开销。当多个核心写入同一个文件时,单个节点的页面缓存会存在争用问题。在内存申请和释放时,需要持有伙伴系统的内存锁。而在内存操作频繁的系统中,该锁的抢占延迟较高。为了管理页面缓存的老化与回收,这些页面会被加入 LRU 链表中。而向 LRU 链表中添加、删除或扫描页面时,均需要持有相应的锁。
在内存资源紧张时,Linux 必须从缓存中回收部分已分配的页面,以释放空间给新分配的页面。对于脏页面来说,回收过程不仅需要将页面从缓存中移除,还需将更改的数据写回存储系统,加重 I/O 负担。为了高效根据文件偏移索引页面缓存,Linux 采用 radix tree 进行管理,但 radix tree 的维护(如插入、删除、查找等操作)也需要额外的计算资源。
为了测量单个线程顺序写入操作的页面缓存开销,我们设计了一个实验,涉及的总 I/O 大小达 2560GiB。实验中,我们使用 IOR 基准 和 perf 分析实用程序来收集和分析相应的性能和跟踪数据。在这一过程中,YRCloudFile Client 将所有 IO 直接发送给后端存储服务(OSS)模块。
我们观察到后端 OSS 模块有超过 40% 的时间被消耗在 pagecache_get_page() 函数上。这是因为该函数只有在收到干净页面后才能返回结果,而这些干净页面必须首先由 kswapd 守护进程回收生成。
因此,kswapd 频繁地启动多个 kworker 后台线程来执行回收任务。由于 OSS 需要将大量数据从存储中读取到内存中,然后将这些数据返回给应用,而这些缓存的数据后续基本不可能被命中。
持续的压力导致系统陷入内存被换入换出的循环中,每次文件读取操作都不得不回收一定量的内存,极大地增加系统负载,同时还推高了 IO 操作的延迟。此外,由于还存在写 IO 压力,这会引起性能抖动,使得存储服务长时间处于低性能状态。
混合 I/O:智能切换访问模式 有效降低系统负载
为了更有效地解决问题,我们引入了一个完全透明的混合 I/O 路径引擎,它可以在 OSS 中自动灵活地切换 BIO 和 DIO。混合 I/O 模式根据 I/O 请求的大小和当前的内存压力来决定是使用 BIO 模式还是 DIO 模式。
BIO 和DIO 转换逻辑示意图
默认的 I/O 模式根据客户业务透传的 open flag 决定。如果默认模式是 BIO,当 I/O 请求的大小大于或等于预设的 IO 阈值时,则自动转换到 DIO 模式。此外,混合 I/O 还会考虑当前机器的内存压力,将文件的缓存页面数量限制在总量的 80%。
当缓存页面或相应的 cgroup 达到其允许限制的 95% 时,系统将切换到 DIO 模式。这样有效地解决了 CPU 占用率高的问题。通常来说,在全闪存环境中,尤其是在长时间持续的大压力读写,或者在缓存命中率很低的情况下,I/O 直接访问 SSD 通常会比读写缓存提供更优的性能。
总体而言,混合 I/O 路径引擎智能地结合了 BIO 和 DIO 的优势,能够根据实际工作负载和系统状态,动态选择最合适的 I/O 模式,从而优化性能并降低系统负载。这一技术创新不仅在实践中展现出了实际的性能提升价值,我们也希望能为行业探索更高效、更灵活的 I/O 管理策略提供借鉴与启示。