web-dev-qa-db-ja.com

ファイルI / Oの高速化:mmap()とread()

150〜200ファイル(4〜10GB)を並行して読み取るLinuxアプリケーションがあります。各ファイルは、通常はそれぞれ2K未満の、さまざまなサイズの小さなブロックで順番に読み取られます。

現在、ファイルのセットを合わせて200 MB/sを超える読み取り速度を維持する必要があります。ディスクはこれをうまく処理します。 1 GB/sを超える要件が予測されています(現時点ではディスクの範囲外です)。

2つの異なる読み取りシステムを実装しました。どちらも_posix_advise_を多用します。1つはmmaped読み取りで、データセット全体をマッピングしてオンデマンドで読み取ります。 2つ目は、read()/seek()ベースのシステムです。

どちらもうまく機能しますが、中程度の場合にのみ、read()メソッドはファイルキャッシュ全体をより適切に管理し、数百GBのファイルを適切に処理できますが、レート制限がひどく、mmapはデータを事前にキャッシュできるため、200MB /秒を超える持続データレートを簡単に維持できますが、データセットの合計サイズが大きい場合は処理できません。

だから私の質問はこれらに来ます:

A:read()タイプのファイルI/Oは、Linuxでの_posix_advise_呼び出しを超えてさらに最適化できますか、またはディスクスケジューラ、VMM、およびposix_advise呼び出しを調整したことで、期待できるほど優れていますか?

B:mmapが非常に大きなマップデータをより適切に処理するための体系的な方法はありますか?

Mmap-vs-reading-blocks は私が取り組んでいるものと同様の問題であり、 mmap-vs-read での議論とともに、この問題の良い出発点を提供しました。 。

44
Bill N.

何に読み返しますか?このデータの最終的な宛先は何ですか?

完全にIOバインドされているように聞こえるので、mmapreadは違いがないはずです。興味深い部分は、データをレシーバーに取得する方法です。 。

このデータをパイプに配置していると仮定すると、各ファイルの内容全体をパイプにダンプすることをお勧めします。ゼロコピーを使用してこれを行うには、 splice システムコールを試してください。また、ファイルを手動でコピーするか、catのインスタンス、または現在のファイルをstdinとして、パイプをstdoutとして大量にバッファリングできるその他のツールをフォークしてみることもできます。

if (pid = fork()) {
    waitpid(pid, ...);
} else {
    dup2(dest, 1);
    dup2(source, 0);
    execlp("cat", "cat");
}

Update0

処理がファイルに依存せず、ランダムアクセスを必要としない場合は、上記のオプションを使用してパイプラインを作成する必要があります。処理ステップは、stdinまたはパイプからのデータを受け入れる必要があります。

より具体的な質問に答えるには:

A:read()タイプのファイルI/Oは、Linuxでのposix_advise呼び出しを超えてさらに最適化できますか、またはディスクスケジューラ、VMM、およびposix_advise呼び出しを調整したことで、期待できるほど優れていますか?

これは、カーネルにユーザースペースから何をすべきかを指示することに関しては最高です。残りはあなた次第です:バッファリング、スレッド化などですが、それは危険で、おそらく非生産的な推測作業です。ファイルをパイプにつなぎ合わせるだけです。

B:mmapが非常に大きなマップデータをより適切に処理するための体系的な方法はありますか?

はい。 以下のオプション は素晴らしいパフォーマンス上の利点をもたらすかもしれません(そしてテストで、mmapをオーバーリードで使用する価値があるかもしれません):

  • MAP_HUGETLB「巨大なページ」を使用してマッピングを割り当てます。

    これにより、カーネルのページングオーバーヘッドが削減されます。これは、ギガバイトサイズのファイルをマッピングする場合に最適です。

  • MAP_NORESERVEこのマッピング用にスワップスペースを予約しないでください。スワップスペースが予約されている場合、マッピングを変更できることが保証されます。スワップスペースが予約されていない場合、使用可能な物理メモリがないと、書き込み時にSIGSEGVが発生する可能性があります。

    これにより、実際に十分な物理メモリとマッピング全体のスワップがない場合でも、実装をシンプルに保ちながら、メモリ不足を防ぐことができます。**

  • MAP_POPULATEマッピング用に(事前障害)ページテーブルにデータを入力します。ファイルマッピングの場合、これによりファイルの先読みが発生します。その後のマッピングへのアクセスは、ページフォールトによってブロックされません。

    これにより、十分なハードウェアリソースがあり、プリフェッチが順序付けられている場合、遅延が発生する可能性があります。このフラグは冗長であると思われます。VFSはデフォルトでこれをより適切に実行する可能性があります。

14
Matt Joiner

おそらく、 readahead システムコールを使用すると、プログラムが読み取りたいファイルフラグメントを事前に予測できる場合に役立つ可能性があります(ただし、これは推測にすぎず、間違っている可能性があります)。

そして、数キロバイトよりはるかに大きなチャンクでデータを読み取るように、アプリケーション、そしておそらくアルゴリズムさえも調整する必要があると思います。代わりに0.5メガバイトになることはできませんか?

ここでの問題は、どのapiが使用されているかではないようです。 mmap()とread()のどちらを使用するかは関係ありませんが、ディスクは指定されたポイントまでシークしてデータを読み取る必要があります(ただし、OSはアクセスの最適化に役立ちます)。

mmap()は、非常に小さいチャンク(数バイト)を読み取る場合にread()よりも優れています。これは、チャンクごとにosを呼び出す必要がないため、非常に遅くなります。

また、Basileが2kb以上を連続して読み取ったように、ディスクが頻繁にそれを探す必要がないようにすることをお勧めします。

1
Tobias Schlegel