27

質問をしてから、自分の回答でフォローアップしたいのですが、他の人がどのような回答をしているかも確認したいと思います。

2 つの別々のスレッドから同時に読み取りたい 2 つの大きなファイルがあります。一方のスレッドは fileA を順次読み取り、もう一方のスレッドは fileB を順次読み取ります。スレッド間でロックや通信は行われず、両方とも可能な限り高速に順次読み取りを行い、読み取ったデータをすぐに破棄します。

Windows でのこのセットアップの経験は非常に貧弱です。2 つのスレッドを合わせたスループットは、2 ~ 3 MiB/秒のオーダーです。ドライブは、ほとんどの時間を 2 つのファイル間で前後にシークしているように見えます。おそらく、各シーク後にほとんど読み取っていません。

スレッドの 1 つを無効にして、一時的に 1 つのスレッドのパフォーマンスを調べると、帯域幅が大幅に向上します (このマシンでは ~45 MiB/秒)。したがって、2 スレッドのパフォーマンスが悪いのは、明らかに OS ディスク スケジューラの影響です。

同時スレッド読み取りのパフォーマンスを改善するためにできることはありますか? おそらく、別の API を使用するか、OS ディスク スケジューラのパラメーターを何らかの方法で微調整することによります。

いくつかの詳細:

ファイルは、2 GiB の RAM を搭載したマシン上でそれぞれ 2 GiB のオーダーです。この質問の目的のために、それらはキャッシュされておらず、完全に最適化されていないと考えています。これが事実であることを確認するために、最適化ツールを使用して再起動しました。

これらのファイルを読み取るために、特別な API は使用していません。この動作は、Win32 の CreateFile、C の fopen、C++ の std::ifstream、Java の FileInputStream など、さまざまなボグ標準 API で再現可能です。

各スレッドはループ内でスピンし、read 関数を呼び出します。各反復で API から要求されるバイト数を、1KiB から 128MiB までの値に変更しました。これを変更しても効果がないため、各ディスク シーク後に OS が物理的に読み取っている量は、この数値によって決定されないことは明らかです。これはまさに期待すべきことです。

1 スレッドと 2 スレッドのパフォーマンスの劇的な違いは、Windows 2000、Windows XP (32 ビットおよび 64 ビット)、Windows Server 2003、およびハードウェア RAID5 の有無にかかわらず再現可能です。

4

6 に答える 6

12

問題は、Windows I/O スケジューリング ポリシーにあるようです。私がここで見つけたものによると、OS がディスク要求をスケジュールする方法はたくさんあります。Linux などはさまざまなポリシーから選択できますが、Vista 以前の Windows は 1 つのポリシー (すべての要求が 64 KB ブロックに分割される FIFO キュー) にロックされていました。このポリシーが、発生している問題の原因であると考えています。スケジューラーが 2 つのスレッドからの要求を混合し、ディスクの異なる領域間で継続的なシークを引き起こします。
さて、良いニュースは、こちらこちらによると、Vista がよりスマートなディスク スケジューラを導入したことです。これにより、リクエストの優先度を設定し、プロセスに最小の不良幅を割り当てることもできます。
悪いニュースは、以前のバージョンの Windows でディスク ポリシーまたはバッファー サイズを変更する方法が見つからなかったことです。また、プロセスのディスク I/O 優先順位を上げると、他のプロセスに対するパフォーマンスが向上したとしても、スレッド同士が競合するという問題が残ります。
私が提案できるのは、自作のディスク アクセス ポリシーを導入してソフトウェアを変更することです。
たとえば、スレッド B で次のようなポリシーを使用できます (スレッド A と同様)。

if THREAD A is reading from disk then wait for THREAD A to stop reading or wait for X ms
Read for X ms (or Y MB)
Stop reading and check status of thread A again  

ステータス チェックにセマフォを使用したり、perfmon カウンタを使用して実際のディスク キューのステータスを取得したりできます。X および/または Y の値は、実際の転送速度を確認してゆっくりと変更することで自動調整することもできます。これにより、アプリケーションがさまざまなマシンや OS で実行されている場合のスループットを最大化できます。そのキャッシュ、メモリ、または RAID レベルを見つけることができます。何らかの形でそれらに影響を与えますが、自動調整を使用すると、すべてのシナリオで常に最高のパフォーマンスが得られます。

于 2008-08-13T13:47:39.333 に答える
6

私の回答にさらにいくつかのメモを追加したいと思います。テストした他のすべてのマイクロソフト以外のオペレーティング システムでは、この問題は発生しません。Linux、FreeBSD、および Mac OS X (異なるハードウェア上の最後の 1 つ) はすべて、1 つのスレッドから 2 つのスレッドに移行すると、総帯域幅の点ではるかに適切に低下します。たとえば、Linux は ~45 MiB/秒から ~42 MiB/秒に低下しました。これらの他のオペレーティング システムは、シークごとにファイルのより大きなチャンクを読み取る必要があるため、ほとんどの時間をディスクでのシークの待機に費やすことはありません。

Windows 向けの解決策は、FILE_FLAG_NO_BUFFERINGフラグをに渡し、CreateFile各呼び出しで大きな (~16MiB) 読み取りを使用することReadFileです。これはいくつかの理由で最適ではありません:

  • このように読み取られた場合、ファイルはキャッシュされないため、キャッシュが通常提供する利点はありません。
  • このフラグを操作するときの制約は、通常の読み取りよりもはるかに複雑です (ページ境界への読み取りバッファーの配置など)。

(最後の意見として。Windows でのスワッピングが非常に地獄である理由はこれで説明できますか? つまり、Windows は、複数のファイルに対して同時に IO を効率的に行うことができないため、他のすべての IO 操作をスワップしている間は、不釣り合いに遅くなります。)


編集して、Will Dean の詳細を追加します。

もちろん、これらの異なるハードウェア構成間で、生の数値は (場合によっては大幅に) 変化しました。ただし、問題は、1 つのスレッドから 2 つのスレッドに移行するときに Windows だけが被る一貫したパフォーマンスの低下です。テストしたマシンの概要は次のとおりです。

  • 単一ドライブで Windows 2000、Windows XP (32 ビット)、および Windows XP (64 ビット) を実行するさまざまな年代の複数の Dell ワークステーション (Intel Xeon)。
  • RAID 1+0 で Windows Server 2003 (64 ビット) を実行する Dell 1U サーバー (Intel Xeon)。
  • Windows XP (64 ビット)、Windows Server 2003、およびハードウェア RAID 5 を搭載した HP ワークステーション (AMD Opteron)。
  • Windows XP (32 ビット)、FreeBSD (64 ビット)、および Linux (64 ビット) を単一のドライブで実行する、ブランドのない自宅の PC (AMD Athlon64)。
  • Mac OS X を実行している自宅の MacBook (Intel Core1)、単一の SATA ドライブ。
  • Linux を実行している自宅のKoolu PC。他のシステムに比べて非常に能力が劣りますが、マルチスレッドのディスク読み取りを行う場合、このマシンでも RAID5 を備えた Windows サーバーよりも優れたパフォーマンスを発揮できることを実証しました。

テスト中、これらすべてのシステムの CPU 使用率は非常に低く、ウイルス対策は無効でした。

前に言い忘れましたが、フラグを設定CreateFileして通常の Win32 APIも試しました。FILE_FLAG_SEQUENTIAL_SCANこのフラグは問題を解決しませんでした。

于 2008-08-12T19:51:06.323 に答える
1

非常に幅広いバージョンの Windows で違いが見られず、単一のドライブとハードウェア RAID-5 の間に違いが見られないのは少し奇妙に思えます。

それは単なる「直感」ですが、これが本当に単純なシークの問題であるかどうかは疑わしくなります。OS X と Raid5 以外は、すべて同じマシンで試しましたか? 別のマシンを試しましたか? このテスト中、CPU 使用率は基本的にゼロですか?

この問題を示す最も短いアプリは何ですか? - ここで試してみたいです。

于 2008-08-12T20:20:17.217 に答える
0

Windows でIOCompletionPortsを使用していますか? C++ 経由の Windows には、このテーマに関する詳細な章があり、運が良ければ、MSDN でも入手できます

于 2008-08-12T21:38:05.017 に答える
0

ある種のメモリ内スレッドセーフロックを作成します。各スレッドは、解放されるまでロックを待機できます。ロックが解放されたら、ロックを取得し、定義された時間または定義されたデータ量の間ファイルを読み取り、他の待機中のスレッドのロックを解放します。

于 2008-08-12T20:04:13.550 に答える
0

ポール - アップデートを見ました。とても興味深い。

Vista または Win2008 で試してみるのも興味深いでしょう。状況によっては、これらの I/O が大幅に改善されたと報告されているようです。

別の API に関する私の唯一の提案は、ファイルのメモリ マッピングを試すことです。試してみましたか? 残念ながら、1 ファイルあたり 2GB では、32 ビット マシンで複数のファイル全体をマップすることはできません。つまり、これはそれほど簡単なことではありません。

于 2008-08-13T09:44:26.017 に答える