![为什么使用 O_DIRECT 标志时读取速度更快?](https://linux22.com/image/788418/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8%20O_DIRECT%20%E6%A0%87%E5%BF%97%E6%97%B6%E8%AF%BB%E5%8F%96%E9%80%9F%E5%BA%A6%E6%9B%B4%E5%BF%AB%EF%BC%9F.png)
我将一个 10GB 的文件复制到我的 SSD 中,其读取带宽约为 3.3GB/s,使用 fio 命令进行了基准测试。以下是参考:https://cloud.google.com/compute/docs/disks/benchmarking-pd-performance
我使用“sync; echo 3 > /proc/sys/vm/drop_caches”清除了缓存。之后,我尝试每次使用系统调用 open() 和 read() 以 3MB 的小块读取文件。如果我不使用 O_DIRECT 和 O_SYNC 打开文件,它会给我大约 1.2GB/s 的带宽。但是,如果我使用 O_DIRECT 和 O_SYNC,它会给我大约 3GB/s 的带宽。即使 O_DIRECT 两次清除缓存也不会真正使用页面缓存。
我的问题是为什么 O_DIRECT 提供正常的 IO 带宽,而没有 O_DIRECT 我就无法获得它。因为从 IO 到页面缓存的数据带宽为 3.3GB/s,而从页面缓存到用户缓冲区的带宽约为 7GB/s。此管道也应该提供正常的 3.3GB/s。为什么它更慢?
我每次都会读取新的 3MB。我没有重复使用数据,因此缓存实际上没什么用。但是管道应该受 IO 约束,为什么事实并非如此?
CPU 是 Intel(R) Xeon(R) Silver 4214 CPU @ 2.20GHz。我不确定 DRAM 速度。但问题是,如果我多次重新读取相同的 3MB,那么我会获得 ~8GB/s 带宽。我想这应该是 DRAM 带宽。因为 linux 可以使用所有可用 RAM 作为页面缓存。
更新
我尝试了启用和未启用 O_DIRECT 的 fio 命令并记录了 iostat。
使用了这个 fio 命令。“fio --name=read_throughput --directory=$TEST_DIR --numjobs=1 --size=10G --time_based --runtime=30s --ramp_time=0s --ioengine=sync --direct=0 --verify=0 --bs=4K --iodepth=1 --rw=read --group_reporting=1 --iodepth_batch_submit=64 --iodepth_batch_complete_max=64”
使用了这个 iostat。
“iostat-j ID nvme0c0n1-x 1”
我得出以下结论:无论使用何种块大小,没有 O_DIRECT 标志的单线程读取都无法使 SSD 饱和,因为读取请求数量不足以达到 3.3GB/s。但是,使用 O_DIRECT 标志时,当块大小为 64M 或更高时,单线程读取能够使设备饱和。在 3M 时,速度约为 2.7GB/s。
现在的问题是,为什么没有 O_DIRECT 标志,CPU 就无法向 SSD 发送足够的读取请求,为什么会限制它们?这与缓存管理限制有关吗?如果是,哪个参数限制了它?我可以更改它并查看它是否会影响发送到设备的读取请求数量吗?
答案1
O_DIRECT 比一般读取更快,因为它绕过了操作系统的缓冲区。您直接从驱动器读取。有几个原因可能会更快,但请记住,在这个级别上,事情变得非常具体。我所说的设置特定因素的示例:如果您有一个针对 NAND 内部 8kB 写入与 4kB 块进行优化的驱动器,并且您以错误的大小进行写入/读取,您将看到一半的性能,但这需要您对驱动器的工作原理有内部了解。这甚至可能在同一个模型中有所不同 - 例如:驱动器的 A 模型可能与驱动器的相同 B 模型具有不同的优化(我在现场多次看到这种情况)
但回到你的问题:
- 无需复制进或出缓存
- 如果你正在做类似 FIO 的事情,你会得到更可预测的读取行为
- 1MB 是一个很大的块大小,因此不处理缓存将为你带来额外的好处
除此之外,您必须开始更深入地研究基准测试,这是一个相当复杂的话题。
我的一般建议是从 开始io_stat
。 是否avgqu-sz
高? 是否util
接近 100%,如果接近驱动器的最大容量,则可能如此。 等待时间长吗? 您有 RAID 吗? 您选择了哪种调度算法? 我见过的导致此类事情的原因有很多,弄清楚究竟是什么原因导致了哪种行为对于您的特定系统来说非常独特。
不过,我一开始说的话可能让你大致了解。最好的猜测是,如果你正在执行大块读取,那么你将节省一些与某种缓存效率低下相关的费用。