io_uringのreadaheadなしはなぜ遅いのか:TursoのPRが示す真実

Io_uring Without Readahead

Tursoにreadaheadを実装するPRが提出され、io_uringのパフォーマンスを測定する機会となりました。O_DIRECTを使うio_uringバックエンドではカーネルのreadaheadが無効になるため、アプリケーション側で実装する必要があります。readaheadを有効にすると、ブロックレイヤーがリクエストをマージし、デバイスへのリクエスト数が大幅に減少します。また、SQポーリングはCPUを消費し、キャッシュミスも増加します。ベンチマークの結果、readaheadなしのio_uringはsyscallバックエンドより遅いことがわかりました。

readaheadを有効にすると、Tursoは23,005多いSQEを発行し、クエリが使わないページを取得して、デバイスがより多くのバイトを読み取ります。しかし、デバイスが受け取るリクエストは少なくなります。
  1. ComputerGuru

    興味深い記事だが、少しパニックになった。ベンチマーク(TPCなど)はここで正しいアプローチを決める手段ではない。それは厳密には、データベース(通常はRDBMS)が動作するハードウェア全体を事実上「所有」している場合にのみ使われるべきだ。組み込みデータベースは、純粋なCRUDアプリケーションのバックエンドとして動作し、サーバーサイドレンダリングやパース、検証などをほぼ行わず、本質的に非同期のHTTP-to-SQLiteインターフェースである場合には、そのように使用されるかもしれない。しかし、組み込みDBは、多くの目的に使用されるマシン(必ずしもサーバーとは限らない)で使用・デプロイされる可能性が高く、マシンが利用できるリソースの範囲内で、また相対的な観点でも最適に動作する必要があり、CPUやメモリ使用量の「価値」のためにパフォーマンスを犠牲にするトレードオフを必然的に行う必要がある。

    これはベンチマークに関してだけでなく、カーネルからキャッシュの所有権を奪う*あらゆる*場面で重要な考慮事項である。カーネルはスタック内で唯一、グローバルな状態を把握でき、メモリプレッシャーがかかったときにメモリを返して全体がうまく連携することを保証できる存在だからだ。これはデータベースやメモリだけに限らない。例えばFreeBSDは、ZFSがカーネルとは別のキャッシュを持っていることに起因する問題を多く抱えてきた(両者の統合が緊密であり、病的な状態に対処するための様々なメカニズムが存在するにもかかわらず)。

  2. marginalia_nu

    ユーザースペースで連続的なreadaheadを行うなら、なぜ単にpreadvを使わないのか? 次の常駐ページまでreadaheadが制限されるが、少なくともMarginaliaのインデックスでの私の実験では、単一のpreadv呼び出しで完全な読み取りができる場合、preadvはio_uringを上回る。

  3. amluto

    なぜシステムコールと、特にO_DIRECTを使ったio_uringの間での選択なのか興味がある。AFAIK TursoはSQLiteのように、複数のプロセスが同じデータベースにアクセスできることをサポートしており、バッファリングが一部のワークロードで大きな利点になると思う。O_DIRECTなしのio_uringの何が問題なのか? RWF_DONTCACHEという中間的な選択肢もある。

  4. ErroneousBosh

    この投稿がフィードに流れてくるたびに「Io_uring without Radiohead」と読んでしまう。いや、できなくはないけど、意味があるだろうか?

  5. weatherlite

    Io_urineはイエロースレッドを使うの?

この日のほかの記事

2026-09-01