Ubuntu Linux 中的异步 IO io_submit 延迟
- 2024-10-12 09:08:00
- admin 原创
- 70
问题描述:
我正在寻找有关如何为在 Ubuntu Linux 14.04 上运行的应用程序获得高效、高性能异步 IO 的建议。
我的应用程序处理事务并在磁盘/闪存上创建一个文件。随着应用程序处理事务,会创建额外的块,这些块必须附加到磁盘/闪存上的文件中。应用程序在处理新事务时还需要频繁读取此文件的块。除了创建必须附加到此文件的新块之外,每个事务可能还需要从此文件中读取不同的块。有一个传入的事务队列,应用程序可以继续处理队列中的事务,以创建足够深的 IO 操作管道,以隐藏磁盘或闪存上的读取访问或写入完成的延迟。对于尚未写入磁盘/闪存的块(由上一个事务放入写入队列)的读取,应用程序将停滞,直到相应的写入完成。
我有一个重要的性能目标 - 应用应尽可能降低发出 IO 操作的延迟。我的应用大约需要 10 微秒来处理每个事务,并准备好向磁盘/闪存上的文件发出写入或读取操作。发出异步读取或写入的额外延迟应尽可能小,以便当只需要文件写入时,应用可以以尽可能接近每笔事务 10 微秒的速率完成处理每个事务。
我们正在试验一种使用 io_submit 发出写入和读取请求的实现。如果您能提供任何建议或反馈,说明最适合我们要求的方法,我将不胜感激。io_submit 能否为我们提供最佳性能,实现我们的目标?每次写入 io_submit 的延迟和每次读取 io_submit 的延迟应该是多少?
使用我们的实验代码(运行在 2.3 GHz Haswell Macbook Pro、Ubuntu Linux 14.04 上),我们在扩展输出文件时测量到写入 io_submit 大约需要 50 微秒。这太长了,我们甚至还没有达到我们的性能要求。任何能帮助我以最小的延迟启动写入请求的指导都将不胜感激。
解决方案 1:
Linux AIO(有时称为 KAIO 或libaio
)是一种暗黑艺术,经验丰富的从业者知道其中的陷阱,但出于某种原因,告诉别人他们还不知道的陷阱是一种禁忌。从网上搜索和经验中,我找到了几个例子,其中 Linux 的异步 I/O 提交io_submit()
可能(悄悄地)变成同步,从而将其变成阻塞(即不再快速)调用:
您正在提交缓冲(又称非直接)I/O。您完全受 Linux 缓存的影响,并且您的提交可以在以下情况下同步:
您正在阅读的内容尚未位于“已读缓存”中。
“写入缓存”已满,在某些现有的写回完成之前,无法接受新的写入请求。
您正在请求对文件系统中的文件进行直接 I/O,但是无论出于何种原因,文件系统决定忽略
O_DIRECT
“提示”(例如,您提交的 I/O 方式不满足O_DIRECT
对齐约束、文件系统或特定文件系统的配置不支持O_DIRECT
),而是选择默默执行缓冲 I/O,从而导致上述情况。您正在对文件系统中的文件进行直接 I/O,但文件系统必须执行同步操作(例如通过写回读取元数据/更新元数据)才能完成您的 I/O。一个常见的例子是发出“分配写入”(例如,因为您正在附加/扩展文件末尾或填充未分配的空洞),这听起来就像提问者正在做的事情(“附加到文件”)。一些文件系统(例如 XFS)会更加努力地提供良好的 AIO 行为,但即使在那里,用户也必须小心避免将某些操作并行发送到文件系统,否则
io_submit()
在另一个操作完成时会再次变成阻塞调用。Seastar框架包含一个文件系统特定案例的小型查找表。您提交了太多未完成的 I/O。您的磁盘/磁盘控制器将具有可同时处理的最大 I/O 请求数,并且内核中每个特定设备都有最大请求队列大小(请参阅
/sys/block/[disk]/queue/nr_requests
文档和未记录的内容)。使I/O 请求积压并超出内核队列的大小会导致阻塞。/sys/block/[disk]/device/queue_depth
如果您提交的 I/O“太大”(例如大于
/sys/block/[disk]/queue/max_sectors_kb
但真正的限制可能小于 512 KiB),它们将在块层内被拆分,并继续处理多个请求。系统全局最大并发 AIO 请求数(参见
/proc/sys/fs/aio-max-nr
文档)也会产生影响,但结果将在io_setup()
而不是中看到io_submit()
。
Linux 块设备堆栈中请求与提交到磁盘之间的一层必须阻塞。例如,Linux 软件 RAID (md) 之类的东西可能会使通过它的 I/O 请求停滞,同时更新各个磁盘上的 RAID 1 元数据。
您的提交导致内核等待,因为:
它需要使用
i_rwsem
正在使用的特定锁(例如)。它需要分配一些额外的内存或分页。
您正在向不是“常规”文件或块设备的文件描述符提交 I/O(例如,您的描述符是管道或套接字)。
上述列表并不详尽。
对于 >= 4.14 内核,该RWF_NONBLOCK
标志可用于使上述某些阻塞场景变得嘈杂。例如,当使用缓冲并尝试读取尚未在页面缓存中的数据时,该RWF_NONBLOCK
标志将导致提交失败,EAGAIN
否则会发生阻塞。显然,您仍然 a) 需要支持此标志的 4.14(或更高版本)内核,并且 b) 必须注意它未涵盖的情况。我注意到有补丁已被接受或被提议EAGAIN
在更多否则会阻塞的场景中返回,但在撰写本文时(2019 年),RWF_NONBLOCK
不支持缓冲文件系统写入。
替代方案
如果您的内核> = 5.1,您可以尝试使用io_uring
它在提交时不会阻塞的效果更好(这是一个完全不同的界面,并且是2020年的新产品)。
参考
AIOUserGuide有一个“性能注意事项”部分,对一些
io_submit()
阻塞/缓慢的情况发出警告。在 ggaoed AoE 目标的 README 的“性能问题”部分中给出了Linux AIO 缺陷的详细列表。
XFS 邮件列表线程“在 io_submit 期间休眠并等待”暗示了一些AIO 队列限制。
“io_submit() 长时间阻塞写入”XFS 邮件列表线程收到 Dave Chiner 的警告:当 XFS 文件系统的占用率超过 85-90% 时,由于缺少大量连续的可用空间,文件系统出现不可预测的延迟的可能性会随着时间推移而增加
ENOSPC
。“[PATCH 1/1 linux-next] ext4:在补丁中添加兼容性标志检查” LKML 线程有来自 Ext4 首席开发人员 Ted Ts'o 的回复,谈论文件系统如何回退到缓冲 I/O
O_DIRECT
而不是调用open()
失败。在“ubifs:允许 O_DIRECT”LKML 线程中,Btrfs 首席开发人员 Chris Mason 表示,当对压缩文件进行请求时,Btrfs 会采用缓冲 I/O
O_DIRECT
。Linux 0.8.0 上的 ZFS 将 ZoL 的行为从错误改为
O_DIRECT
“接受”,方法是回退到缓冲 I/O (参见提交消息中的第 3 点)。在 Linux 上的 ZFS“直接 IO”GitHub 问题中,从提交前开始还有进一步的讨论。在“NVMe 读取 ZFS 性能问题(submit_bio 到 io_schedule)”问题中,有人建议他们即将提交一项启用正确零拷贝的更改O_DIRECT
。如果这样的更改被接受,它将出现在 ZoL 0.8.2 以上的某个未来版本中。Ext4 wiki 有一个警告,某些 Linux 实现(哪个?)
O_DIRECT
在执行分配写入时会回退到缓冲 I/O。
2004 年 Linux 可扩展性努力页面上的标题为“内核异步 I/O (AIO) 支持”,其中列出了Linux AIO 中有效和无效的事物(有点旧,但可以作为快速参考)。
在“io_submit() 需要很长时间才能返回?”linux-aio 邮件列表主题中,Zach Brown 解释说,对于文件系统中的文件来说,要找出要对其发出异步 I/O 的块,它必须同步读取元数据(但这些数据通常已被缓存)。
LKML 上的附信“[PATCH 0/10 v13] 合并请求:无等待 AIO”
io_submit()
列出了延迟的原因。还有一篇LWN 文章讨论了无等待 AIO 补丁集的早期版本以及它未涵盖的一些情况(但请注意,它最终涵盖了缓冲读取)。在 linux-aio 邮件列表上搜索“io_submitblocking”
有关的:
Linux AIO:扩展性较差
io_submit() 会阻塞,直到前一个操作完成
Linux 上的缓冲异步文件 I/O(但请坚持明确讨论 Linux 内核 AIO 的部分)
希望这篇文章能对某些人有所帮助(如果对您有帮助,您可以点赞吗?谢谢!)。
解决方案 2:
我在此以Boost.AFIO 提议的作者身份发言。
首先,除非 O_DIRECT 已打开且不需要分配扩展区,否则 Linux KAIO (io_submit) 几乎总是处于阻塞状态,如果 O_DIRECT 已打开,则您需要在 4Kb 对齐边界上读取和写入 4Kb 倍数,否则您将强制设备执行读取-修改-写入。因此,除非您重新设计应用程序以使其支持 O_DIRECT 和 4Kb 对齐的 i/o,否则使用 Linux KAIO 将一无所获。
其次,永远不要在写入期间扩展输出文件,否则会强制进行范围分配,甚至可能进行元数据刷新。相反,将文件的最大范围分配为某个适当大的值,并保留文件末尾的内部原子计数器。这应该会将问题简化为仅进行范围分配,对于 ext4 而言,这是批量且懒惰的 - 更重要的是,您不会强制进行元数据刷新。这应该意味着 ext4 上的 KAIO 在大多数情况下将是异步的,但不可预测的是,它会在将延迟的分配刷新到日志时同步。
第三,我可能解决您问题的方法是使用原子追加(O_APPEND),不使用 O_DIRECT 或 O_SYNC,因此您要做的就是将更新追加到内核页面缓存中不断增长的文件中,这样速度非常快,并且并发安全。然后,您会不时地对日志文件中过时的数据进行垃圾收集,这些数据的范围可以使用 fallocate(FALLOC_FL_PUNCH_HOLE)释放,这样物理存储就不会永远增长。这将合并写入存储的问题推到了内核上,内核已经花费了大量精力来加快这一速度,而且由于它是一种始终向前的写入,您将看到写入以与写入顺序相当接近的顺序命中物理存储,这使得断电恢复变得简单。后一种选择是数据库和日记归档系统所采用的方式,尽管您可能需要对您的软件进行大量重新设计,但事实证明,这种算法是通用问题情况下延迟与耐用性的最佳平衡。
如果以上所有操作看起来工作量很大,那么操作系统已经将所有三种技术整合到一个高度优化的实现中,该实现更广为人知,即内存映射:4Kb 对齐 i/o、O_DIRECT、永不扩展文件、所有异步 i/o。在 64 位系统上,只需将文件分配到非常大的空间并将其映射到内存中即可。根据需要进行读写。如果您的 i/o 模式混淆了内核页面算法(这种情况可能会发生),您可能需要偶尔使用 madvise() 来鼓励更好的行为。相信我,madvise() 少即是多。
很多人尝试使用各种 O_DIRECT 算法复制 mmap,却没有意识到 mmap 已经可以满足您的所有需求。我建议先探索这些算法,如果 Linux 不适用,请尝试使用具有更可预测的文件 i/o 模型的 FreeBSD,然后再深入研究自己的 i/o 解决方案。作为一个整天都在做这些事情的人,我强烈建议您尽可能避免使用它们,文件系统是怪异行为的魔鬼深渊。把永无止境的调试留给别人吧。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件