首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 汽车 游戏 社会热点 开源推荐 医疗健康 归档 标签 关于

没有预读的 Io_uring

2026-09-01 1 阅读 约7分钟阅读 porridgeraisin
分享:
字号:
有人打开了一个 PR 来在 Turso 中实现预读。这是一个一次性的实现,但却是测量 io_uring 和了解更多信息的一个很好的借口。 Turso 有两个后端。系统调用使用 pread(2)。 io_uring 使用 io_uring,并使用 O_DIRECT 打开数据库文件,没有使用缓冲 I/O 的选项。 O_DIRECT 取消了内核预读,因此取回它意味着在应用程序中实现它。公关的结果令人印象深刻。带有应用程序缓冲区的 io_uring 速度更快。我想明白为什么。如果没有预读,io_uring 一次仅发出一个条目。问题是缺乏并发性:每次读取都会等待前一个读取。应用程序知道“嘿,此时,我需要第 100 页”,这意味着:Turso 提交第 100 页的读取 SQE,然后等待它。扫描继续。现在 Turso 需要第 101 页,因此它会提交一个新的读取该页的 SQE。随着预读的开启,事情发生了变化。 Turso 需要第 100 页,检测顺序访问,并且不是请求一页,而是提交第 100 页到第 131 页的读取。现在同时进行 32 个读取。以下测量在 1.2 GiB 数据库上使用 TPC-H(分析数据库的标准基准)。 Q6,我测量的查询,对 lineitem(基准测试中最大的表)进行了全面扫描。 off 表示 PRAGMA prefetch_pages=0 :没有预读的 PR 代码。 on 表示 32 页的窗口。请求合并 我想看看 I/O 路径中的预读发生了什么变化:Turso 提交了多少请求,以及什么到达了设备。因此,我与 Q6 一起运行 iostat -dxm 1 sda,并使用 perf stat 计算 io_uring:io_uring_submit_req 跟踪点。 off (n=1) on (n=1) SQE 提交 195,207 218,212 个设备请求 ~196,000 ~16,300rareq-sz 4.37 KiB 56.53 KiB %rrqm ~0 91-93%rareq-sz 是到达设备的读取请求的平均大小。 %rrqm 是在发出之前与另一个请求合并的读取请求的百分比。打开预读后,Turso 会多发出 23,005 个 SQE,并且它会获取比需要更多的页面,从而使设备读取更多字节。但设备收到的请求较少。如果队列中的两个请求覆盖彼此相邻的扇区,则块层会将它们合并为一个更大的请求。仅当两个请求同时在队列中时这才有效。我用 perf 计算了合并跟踪点。关闭预读后,195,516 个 BIOS 中只有 140 个被合并。这是有道理的,因为队列中只有一个 SQE,并且没有任何内容可以合并。打开预读后,218,493 个 BIOS 中的 202,539 个被合并,设备仅收到 15,951 个请求。由于队列中有多个SQE,内核将它们合并。轮询线程 Turso 将 io_uring 与 sqpoll 结合使用。 sqpoll 使用一个线程来旋转检查工作是否已交付以及内核是否应该执行某些操作。它是一个以旋转为代价来跟踪工作的线程。我使用 prefetch_pages=32 对 sqpoll 的执行进行计时,以查看时间花费在哪里:8.22 秒的挂机时间、3.70 秒的用户时间和 8.46 秒的系统时间(七次运行的中位数)。系统时间大于挂机时间,并且只有当两个线程同时使用 CPU 时才可能出现这种情况。我预计循环会出现在查询代码中,但是: io_uring 后端上的 Q6 带有 SQ 轮询。 io_sq_thread(内核轮询线程)处于周期的 65%。因此,我在没有 SQ 轮询的情况下重建了 Turso,用普通的 IoUring::new 替换了 setup_sqpoll 构建器,这已经是当 sqpoll 设置失败时 Turso 的后备。普通/默认振铃:墙壁时间 8.62 秒,用户时间 3.62 秒,系统时间 1.27 秒。删除轮询线程使挂起时间变得更糟,并且系统时间更短。系统时间不为零,因为每次提交我们仍然调用 io_uring_enter(2),并且 Turso 一次提交一个 SQE。轮询是否值得取决于机器。迪多纳等人。在 2022 年 SYSTOR 论文中对此进行了测量:使用一个 NVMe 驱动器和一个 CPU 核心进行提交轮询仅达到 13 KIOPS(每秒 13,000 次 IO 操作)——两个线程必须共享一个核心,因此它们轮流进行。有了第二个核心,性能就完全恢复了。我使用的这个盒子有 4 个 vCPU。该查询是单线程的,使用一个环和一个轮询线程,并且轮询线程始终有一个空闲核心,因此它永远不会与查询竞争 CPU。缓存未命中 随着 SQ 轮询关闭,我在两个后端再次对 Q6 进行计时:io_uring 为 8.55 秒,系统调用为 3.02 秒。差异是显着的。我想了解额外的时间去哪儿了,所以我用 perf 计算了指令和缓存未命中数。关于此表中的计数器的一个注意事项:它们来自重建的主机(谁愿意为空闲时间向 Hetzner 付费?),因此它们的绝对值与上面的时间不匹配。后端周期(中位数,n=7) 指令(中位数,n=7) IPC 缓存未命中 未命中率 io_uring plain 5.374 B 21.497 B 3.999 10.666 M 13.255% 系统调用 4.734 B 21.297 B 4.499 5.889 M 7.691% io_uring 运行 0.2 B更多的指令几乎没有什么,但它需要多 4.8 M 的高速缓存未命中。两者都 bac
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱