考虑一个CPU受限的应用程序,但也有高性能I/O需求。
我正在比较Linux和Windows的文件I/O,我看不出epoll对Linux程序有什么帮助。内核会告诉我文件描述符“准备好阅读了”,但我仍然需要调用blocking read()来获取数据,如果我想读取兆字节,很明显这会阻塞。
在Windows上,我可以创建一个设置为OVERLAPPED的文件句柄,然后使用非阻塞I/O,并在I/O完成时收到通知,并使用该完成函数中的数据。我不需要花费应用程序级别的挂钟时间等待数据,这意味着我可以精确地将线程数量调整为核心数量,并获得100%的CPU利用率。
如果我必须在Linux上模拟异步I/O,那么我必须分配一定数量的线程来做这件事,这些线程将花费一点时间做CPU的事情,并且大量的时间阻塞I/O,再加上与这些线程之间的消息传递会产生开销。因此,我将过度订阅或未充分利用我的CPU核心。
我把mmap()+ madvise()(WILLNEED)看作是一个“穷人的madvise I/O”,但它仍然没有完全实现,因为当它完成时我不能得到通知--我必须“猜测”,如果我猜错了,我将最终阻塞内存访问,等待数据来自磁盘。
Linux似乎在io_submit中开始了POSIX I/O,它似乎也有一个用户空间POSIX aio实现,但它已经这样一段时间了,我知道没有人会为这些系统的关键,高性能应用程序担保。
Windows模型的工作原理大致如下:
1.发出异步操作。
1.将异步操作绑定到特定的I/O完成端口。
1.等待该端口上的操作完成
1.当I/O完成时,等待端口的线程解除阻塞,并返回对挂起的I/O操作的引用。
第1/2步通常是单独完成的。第3/4步通常是用一个工作线程池完成的,而不一定是发出I/O的同一个线程。这个模型有点类似于boost::asio提供的模型,除了boost::asio实际上并不给予异步的基于块的(磁盘)I/O。
与Linux中epoll的不同之处在于,在第4步中,还没有发生I/O--它将第1步提升到第4步之后,如果您确切地知道自己需要什么,那么这就是“向后”。
在编写了大量的嵌入式、桌面和服务器操作系统之后,我可以说这种异步I/O模型对于某些类型的程序来说是非常自然的。它还具有非常高的吞吐量和低开销。我认为这是Linux I/O模型在API级别上剩余的真实的缺点之一。
4条答案
按热度按时间4uqofj5v1#
(2020)如果您使用的是5.1或更高版本的Linux内核,则可以将
io_uring
interface用于类似文件的I/O,并获得出色的异步操作。与现有的
libaio
/KAIO接口相比,io_uring
具有以下优点:liburing
助手库时)recvmsg()
/sendmsg()
从>=5.3开始支持,请参阅在io_uring.c's git history中提到单词support的消息)read
/write
的异步操作的支持不断增加(例如fsync
(>=5.1)、fallocate
(>=5.6)、splice
(>=5.7)等)与glibc的POSIX AIO相比,
io_uring
具有以下优点:*更快更高效(上述更低的开销优势在这里更加适用)
io_uring
肯定可以!Efficient IO with io_uring文档详细介绍了
io_uring
的优点和用法。What's new with io_uring文档描述了在5. 2 - 5. 5内核之间添加到io_uring
的新功能,虽然The rapid growth of io_uring LWN article描述了5.1 - 5.5内核中的每个内核都有哪些功能,(另请参阅LWN的io_uring文章列表)。还有io_uring
作者Jens Axboe从2019年底开始的Faster IO through io_uring Kernel Recipes videoed presentation(slides)和从2022年中期开始的What’s new with io_uring Kernel Recipes videoed presentation(slides)。最后,Lord of the io_uring tutorial介绍了io_uring
的用法。io_uring
社区可以通过io_uring邮件列表和io_uring mailing list archives显示2021年初的每日流量。关于“在
recv()
与read()
的意义上支持部分I/O“:一个patch went into the 5.3 kernel that will automatically retryio_uring
short reads和一个进一步的提交进入了5.4内核,将行为调整为only automatically take care of short reads when working with "regular" files on requests that haven't set theREQ_F_NOWAIT
flag。(看起来你可以通过IOCB_NOWAIT
请求REQ_F_NOWAIT
,或者用O_NONBLOCK
打开文件)。因此,你可以得到recv()
样式-“短”I/io_uring
的O行为。使用
io_uring
的软件/项目虽然这个接口还很年轻(它的第一个版本于2019年5月发布),但一些开源软件正在“野外”使用
io_uring
:fio(也是由Jens Axboe编写的)有一个io_uring ioengine后端(事实上,它是从2019年2月开始在fio-3.13中引入的!)。“Improved Storage Performance Using the New Linux Kernel I/O Interface SNIA presentation“(slides)两名英特尔工程师表示,他们能够在一个工作负载上获得两倍的IOPS,而在另一个工作负载上,在队列深度为1时,平均延迟不到一半。
io_uring
ioengine到Optane设备上的libaio
ioengine。在其v19.04版本中用于块设备访问的SPDK project added support for using io_uring(!)(但显然这不是您通常使用SPDK进行基准测试以外的后端)。最近,他们似乎也在v20.04中添加了support for using it with sockets...
Ceph committed an io_uring backend在2019年12月发布,这是其15.1.0版本的一部分。提交作者发布了一条github评论,显示一些io_uring后端与libaio后端相比有一些胜利和损失(在IOPS,带宽和延迟方面)取决于工作负载。
RocksDB committed an
io_uring
backend for MultiRead于2019年12月发布,是其6.7.3 release的一部分。Jens表示io_uring
helped to dramatically cut latency。libev released 4.31 with an initial
io_uring
backend在2019年12月。虽然作者的一些原始观点在新的内核中得到了解决,但在撰写本文时(2021年中期),libev's author has some choice words aboutio_uring
's maturity正在采取观望态度,然后再实施进一步的改进。QEMU committed an io_uring backend于2020年1月发布,是QEMU 5.0 release的一部分。在“io_uring in QEMU: high-performance disk IO for Linux“PDF演示文稿中,Julia Suvorova展示了
io_uring
后端在一个随机16 K块的工作负载上的性能优于threads
和aio
后端。Samba merged an
io_uring
VFS backend于2020年2月发布,是桑巴舞4.12版本的一部分。在"Linux io_uring VFS backend."桑巴舞邮件列表线程中,斯特凡·梅茨马赫(提交作者)说,io_uring
模块能够将吞吐量提高大约19(与一些未指定的后端相比)。您也可以阅读Stefan的"Async VFS Future" PDF演示文稿,了解更改背后的一些动机。Facebook's experimental C++ libunifex使用它(但您还需要5.6+内核)
Rust的人一直在编写 Package 器,使
io_uring
更容易被纯Rust访问。rio是一个库,作者说他们achieved higher throughput compared to using sync calls wrapped in threads。作者在FOSDEM 2020上给出了一个presentation about his database and library,其中包括一个部分,赞扬了io_uring
的优点。rust library Glommio专门使用
io_uring
。作者(Glauber Costa)发表了一份名为Modern storage is plenty fast. It is the APIs that are bad的文档,显示在Optane设备上执行顺序I/O时,通过仔细调整Glommio可以获得超过常规(非io_uring
)系统调用2. 5倍的性能。2020年10月的Gluster merged an io_uring posix xlator,是Gluster 9.0 release的一部分。提交作者提到性能“不比常规的pwrite/pread系统调用差”。
使用
io_uring
进行软件调查io_uring
改进背后的驱动力之一(例如workaround to reduce for filesystem inode contention)。有一个演示"Asynchronous IO for PostgreSQL"(注意视频在5分钟之前会中断)(PDF)激发了对PostgreSQL更改的需求,并展示了一些实验结果。他表达了hope of getting his optionalio_uring
support into PostgreSQL 14,似乎敏锐地意识到了什么是在2020年12月,Andres在“Blocking I/O,Blocking I/O and io_uring”pgsql-hackers邮件列表线程中进一步讨论了他的PostgreSQLio_uring
工作,并提到正在进行的工作可以在https://github.com/anarazel/postgres/tree/aio中看到。io_uring
supportio_uring
support但它在项目中的进展缓慢io_uring
support for eventing(但不是系统调用)将于2020年4月发布,Linux: full io_uring I/O问题概述了进一步集成它的计划io_uring
Linux发行版支持io_uring
系统调用。此发行版没有预打包liburing
助手库,但您可以自行构建它。io_uring
系统调用。如上所述,发行版没有预先打包liburing
。liburing
,所以io_uring
是可用的。io_uring
系统调用。这个发行版没有预先打包liburing
助手库,但是你可以自己构建它。io_uring
(此答案的先前版本错误地说它支持)。有一个Add io_uring support Red Hat knowledge base article(内容位于订阅者付费墙后面)正在“进行中”。io_uring
.内核足够新(5.14),但对io_uring
的支持被显式禁用。希望
io_uring
能为Linux带来更好的异步文件式I/O。(To在过去的某个时候,Jens Axboe(Linux内核块层维护者和
io_uring
的发明者)thought this answer might be worth upvoting:-)dffbzjpn2#
Peter Teoh间接指出的真实的答案是基于io_setup()和io_submit()。具体来说,Peter指出的“aio_”函数是基于线程的glibc用户级仿真的一部分,这不是一个有效的实现。真实的答案是:
字符串
请注意,2012-08年的手册页上说,这个实现还没有成熟到可以取代glibc用户空间仿真的地步:
http://man7.org/linux/man-pages/man7/aio.7.html
这个实现还没有成熟到可以使用内核系统调用完全重新实现POSIX AIO实现的程度。
因此,根据我能找到的最新内核文档,Linux还没有一个成熟的、基于内核的异步I/O模型,而且,如果我假设文档中的模型实际上是成熟的,它仍然不支持recv()和read()意义上的部分I/O。
qlckcl4x3#
正如解释:
http://code.google.com/p/kernel/wiki/AIOUserGuide
这里:
http://www.ibm.com/developerworks/library/l-async/的
Linux在内核级别提供了Java数据块I/O,API如下:
字符串
如果你问谁是这些API的用户,那就是内核本身-这里只显示了一小部分:
型
等
在用户空间级别,还有io_submit()等API(来自glibc),但以下文章提供了使用glibc的替代方案:
http://www.fsl.cs.sunysb.edu/~vass/linux-aio.txt的
它直接将io_setup()等函数的API实现为直接syscall(绕过glibc依赖),通过相同的“_NR_io_setup”签名的内核Map应该存在。
http://lxr.free-electrons.com/source/include/linux/syscalls.h#L474适用于最新版本3.13)欢迎您在内核中直接实现这些io*()API:
型
最新版本的glibc应该使这些使用“syscall()”来调用sys_io_setup()变得不必要,但是如果没有最新版本的glibc,如果您使用的是具有这些“sys_io_setup()”功能的最新内核,您总是可以自己进行这些调用。
当然,异步I/O还有其他用户空间选项(例如,使用信号?):
http://personal.denison.edu/~bressoud/cs375-s13/supplements/linux_altIO.pdf的
或者说:
What is the status of POSIX asynchronous I/O (AIO)?的
“io_submit”和friends在glibc中仍然不可用(参见io_submit手册页),我已经在我的Ubuntu 14.04中验证了这一点,但这个API是Linux特有的。
其他像libuv、libev和libevent也是异步API:
http://nikhilm.github.io/uvbook/filesystem.html#reading-writing-files
http://software.schmorp.de/pkg/libev.html的
http://libevent.org/
所有这些API都旨在跨BSD、Linux、MacOSX甚至Windows移植。
在性能方面,我还没有看到任何数字,但怀疑libuv可能是最快的,由于其重量轻?
https://ghc.haskell.org/trac/ghc/ticket/8400
ql3eal8s4#
对于网络套接字i/o,当它“就绪”时,它不会阻塞。这就是
O_NONBLOCK
和“就绪”的意思。对于磁盘I/O,我们有posix aio,linux aio,sendfile和朋友。