この質問は、私が投稿した以前の質問: Windows fsync (FlushFileBuffers) performance with large filesのフォローアップです。可能な解決策だけでなく、新しい質問も見つけた場所。
fsynced 書き込みのさまざまなシナリオをベンチマークしているときに、いくつかの驚くべき結果が見つかりました。誰かが説明を手伝ったり、これらの結果を説明する情報の方向性を教えてくれることを願っています.
このベンチマークが行うことは、8 ページ (32 K) のバッチでファイルにランダム ブロック (4096 バイトのページ) を順次書き込み、書き込みをフラッシュすることです。合計 200000 ページを書き込み、合計 800 MB と 25000 回のフラッシュに相当します。ファイルのサイズは、書き込みを開始する前に最終的な長さに設定されます。
合計 4 つのオプションがサポートされており、そのすべての組み合わせが実行されます。
FlushFileBuffers
バッチまたは通常のフラッシュ (NS) を書き込んだ後、「fsync」/操作 (FS) を実行します。- 書き込みを開始する前にファイルの最後の位置に 1 バイトを書き込む (LB) か、ファイルを空のままにする (E)。
- 通常のバッファ付き書き込み (B) またはバッファなし/ライトスルー (WT) 書き込みを使用するには (FILE_FLAG_NO_BUFFERING および FILE_FLAG_WRITE_THROUGH を使用)。
- ファイル ハンドル (F) を介してファイル ストリームに直接書き込むか、メモリ マップ (MM) を使用して間接的にファイルに書き込みます。
以下の表は、これらのオプションのすべての組み合わせについて、私のシステム (低速のスピンドル ディスクを備えた 64 ビットの Win 7 ラップトップ) での調査結果をまとめたものです。
私が見つけたのは、「fsynced」バッファリングされた書き込みのパフォーマンスがファイルのサイズに応じて指数関数的に低下し、スループットが非常に低くなり、大きなファイルと組み合わせてこれを実行できないことです。ファイルの最後のバイトが書き込まれた場合 (オプション LB)、スループットはさらに低下するため、順次書き込みシナリオではなくランダム書き込みシナリオでは、パフォーマンスがさらに劇的になるのではないかと心配しています。
しかし驚くべきことに、バッファなし/ライトスルー I/O では、ファイルのサイズに関係なく、スループットは一定のままです。最初 (最初の 100 ~ 200 MB) は、バッファリングされた書き込みよりもスループットが低くなりますが、その後は平均スループットがすぐに追いつき、800 MB の書き込みが大幅に速く終了します。さらに驚くべきことは、ファイルの最後のバイトが書き込まれた場合、スループットが 2 倍に増加することです。
メモリ マップされたファイルを介してファイルに書き込む場合、同じ指数関数的なパフォーマンスの低下が見られます。また、ファイルが unbuffered/writethrough フラグで開かれた場合も同様です。ここでも、ファイルの最後の位置にバイトが書き込まれていると、パフォーマンスが低下します。
UPDATE ハワードの説明hereおよびhereに基づいて、書き込みを開始する前に新しいファイルを作成せずにテストを再実行しました (つまり、既存の完全に書き込まれたファイルを開いて上書きします)。このテストで行われた変更を反映するために、元の質問のコードを更新しました。この結果は、Linux に関する彼の説明と調査結果と部分的に一致しています。ただし、いくつかの注目すべき例外があります。下の表は結果を示しています。赤は重要な変化を強調し、青は変化が起こらなかった場所を強調しています。これは驚くべきことです (つまり、ハワードの説明で言及された効果だけが作用した場合、期待と一致しません)。
「fsync」フラッシュを使用したバッファリングされたファイルへの書き込み (つまり、memmap 経由ではない) の場合、パフォーマンスは指数関数的に減衰するものから一定の傾向に変わりました。ただし、以前のテスト シナリオよりもはるかに時間がかかります。スループットは一定の 1.5 MB/秒ですが、以前は約 20 MB/秒で始まり、約 1.5 MB/秒まで指数関数的に減衰しました。考えられる説明は、フラッシュのたびにファイル メタデータもフラッシュされ、メタデータの場所を探すためにディスク全体が一回転することです。
ファイル シナリオへの「ライト スルー」の場合、最後のバイトを書き込むかどうかの結果は、Howard の説明から予想される内容と一致するようになりました。
ただし、1 つの注目すべき例外を除いて、メモリ マップへの書き込みは実際には変わっておらず、これは驚くべきことです。書き込みパフォーマンスは依然として同じ指数関数的な減衰を示しています (約 20 MB/秒から始まり、1.8 MB/秒まで減衰します)。これは、別のメカニズムが働いていることを示唆しています。1 つの注目すべき例外は、基になるファイルが FILE_FLAG_WRITE_THROUGH なしで作成され、「fsync」フラッシュが実行される場合です。このシナリオでは、約 1.6 MB/秒のスループットで一定の (低い) パフォーマンスが示されています。疑問があったので、このシナリオを何度も再実行しましたが、毎回同じ結果が得られました。
もう少し詳しく調べるために、小さいファイル (50000 ページ、200 MB に相当) を使用してこのテストを再実行し、fsync のパフォーマンス (バッファリングされた I/O の場合) が実際にはファイル サイズに依存することを確認しました。結果を以下に示します。特別な注意が必要なものは赤で強調表示されています。
これらの結果は、より大きなファイルで見られたものとよく相関しています。注目すべき変更点は、強調表示されている書き込みのパフォーマンスが少し向上していることです。ここでは、約 7 MB/s の制限に達しているようです。
これまでの私のシステムでの観察に基づいて、非常に推測的な結論として要約します。
- バッファリングされた IO (つまり、FILE_FLAG_WRITE_THROUGH フラグなし) を使用するファイルの Windows での "fsync" パフォーマンスは、ファイルに既に書き込まれているバイト数に応じて指数関数的に低下します。その理由は、ファイルのメタデータを毎回フラッシュする必要があるためと思われ、これによりファイルの先頭へのディスク シークが発生します。
- メモリ マップ ファイルに書き込むときの Windows での "fsync" パフォーマンスも、指数関数的に低下するパフォーマンスを示します。現在、これを引き起こしている正確なメカニズムについての説明はありません。
この観察されたパフォーマンスを考えると、少なくとも私のユース ケースでは、これら 2 つの I/O オプションは実行可能なソリューションにはなりません。
Greg の提案に従って、Windows のディスク キャッシュをオフにしてテストを再実行し、Howard が提供するベンチマーク コードを実行して、自分自身のエラーが原因で結果が歪む可能性を排除します。
更新 2 テストを完了し、現在結果をまとめています。「の完全な歴史」を書かないために、この質問の現在の内容を、結果、調査結果、およびいくつかの結論の要約に置き換えます。この質問に対する Howard の回答と、.NET コードの隣で彼の C ベンチマーク コードを実行できる機能は、最も役に立ちました。これらの結果とアプリケーションの結果は、非常によく相関しています。Rlb の回答は、ディスクに関連する「合理的な数値」とは何かをよりよく理解するのに役立ちました。ありがとう。
質問の一部は未回答のままです。特に、メモリ マップへの書き込み時に観測された (ファイル サイズに依存する) パフォーマンスの低下に関連しています。シーク/メタデータのフラッシュに関連している可能性がありますが、その理由/方法はまだ明らかではありません。