計算負荷の高いタスクを実行しながらデータをディスクに保存する効率的な方法
-
06-07-2019 - |
質問
非常にCPUを集中的に使用する(procバウンド)科学ソフトウェアに取り組んでいますが、かなり頻繁にディスクにデータを書き込む必要があります(i / oバウンド)。
この(OpenMP)に並列化を追加しているので、ディスクへの書き込みのニーズに対処する最善の方法は何かと思っています。 シミュレーションがHDDで待機する必要はありません(これは現在実行中です)。
このための「ベストプラクティス」を探しています。速度が最も重要なことです(これらは非常に長いシミュレーションになる可能性があります)。
ありがとう 〜アレックス
最初の考え:
別のプロセスを持つことで、ディスクへの実際の書き込みが行われるため、シミュレーションには2つのプロセスがあります。1つはCPUバインド(シミュレーション)で、もう1つはIOバインド(ファイルの書き込み)です。これは複雑に聞こえます。
おそらくパイプ/バッファですか?私はこれらに新しいので、多分それは可能な解決策かもしれません。
解決
OpenMPをプログラムに実装する場合、並列セクションの #pragma omp single または #pragma omp master を使用してファイルに保存することをお勧めします。これらのプラグマでは、1つのスレッドのみが何かを実行できます。したがって、コードは次のようになります。
#pragma omp parallel
{
// Calculating the first part
Calculate();
// Using barrier to wait all threads
#pragma omp barrier
#pragma omp master
SaveFirstPartOfResults();
// Calculate the second part
Calculate2();
#pragma omp barrier
#pragma omp master
SaveSecondPart();
Calculate3();
// ... and so on
}
ここではスレッドのチームが計算を行いますが、単一のスレッドのみが結果をディスクに保存します。
ソフトウェアパイプラインのように見えます。 Intel Threading Building Blocksライブラリのtbb :: pipelineパターンを検討することをお勧めします。 http://cache-www.intel.com/cd/00/00/30/11/301132_301132.pdf#page=25 。 4.2項をお読みください。彼らは問題を解決しました:1つのスレッドはドライブから読み取り、2つ目は読み取り文字列を処理し、3つ目はドライブに保存します。
他のヒント
最善の方法は、完全に新しいプロセスではなく、データを保存するために別のスレッドを作成することです。新しいプロセスでは、プロセスの境界を越えて保存するデータを通信する必要があるという問題が発生します。これにより、新しい問題が発生します。
頭に浮かぶ最初の解決策は、あなたが言ったこととほぼ同じです-シムからライターへの一方向のパイプを使用して、独自のプロセスでディスク書き込みを行います。ライターは、可能な限り高速で書き込みを行います(パイプから新しいデータを引き出します)。これに関する問題は、シムがライターよりも先に進みすぎると、シムがパイプの書き込みをブロックすることになり、削除時にI / Oがバインドされることです。
問題は、実際にはシミュレーションサイクルが結果を吐き出すまで完了しないことです。
2番目に起こることは、非ブロッキングI / Oを使用することです。シムが書き込む必要があるときはいつでも、ノンブロッキングI / Oを介して行う必要があります。次に書き込む必要がある場合、新しいI / O操作を開始する前に、以前のI / O操作の結果を取得することができます(おそらくわずかな待機が発生します)。これにより、シミュレーションを書き込みよりもはるかに先に進めることなく、I / Oと並行して可能な限りシミュレーションを実行し続けます。
最初の解決策は、シミュレーションの処理サイクルが変動する場合(書き込みの時間よりも短い場合があり、長い場合もあります)の方が良いでしょう。
処理サイクルが常に(またはほぼ常に)書き込み時間より短くなる場合 パイプを使用せずにノンブロッキングI / Oを使用することもできます。パイプを使用すると最終的に満杯になり、シムがI / Oでハングアップするためです。
あなたはCPUとIOに縛られているので、推測させてください:まだ十分なメモリがありますよね?
その場合、メモリ内のディスクに書き込む必要があるデータを一定の範囲でバッファリングする必要があります。通常、大量のデータを書き込む方が、小さい部分を書き込むよりもはるかに高速です。
書き込み自体:メモリマップIOの使用を検討してください。ベンチマークを行ってからしばらく経ちましたが、前回行ったときはかなり速くなりました。
また、CPUとIOを少しだけトレードすることもできます。現在、何らかの生の非圧縮データとしてデータを書き込んでいると思いますか?単純な圧縮スキームを使用して書き込まれるデータの量を減らすと、IOパフォーマンスが得られる場合があります。 ZLIBライブラリは操作が非常に簡単で、最低の圧縮レベルで非常に高速に圧縮します。データの性質に依存しますが、冗長性が非常に大きい場合は、非常に粗雑な圧縮アルゴリズムでもIOバウンドの問題を解消できます。
1つのスレッドは、計算負荷の高いプロセスのステップを継続的に実行し、部分結果を部分結果のキューに追加します。別のスレッドは、部分的な結果をキューから継続的に削除し、ディスクに書き込みます。キューへのアクセスを同期してください。キューはリストのようなデータ構造で、アイテムを最後に追加したり、アイテムを前面から削除したりできます。
アプリケーションにCPU用とハードディスク用の2つのスレッドを作成します。
CPUスレッドが、完了したデータをキューにプッシュし、データが入ってくるとハードディスクスレッドがそこからプルします。
この方法では、CPUがデータを削除し、他の誰かがそれを処理できるようにし、ハードドライブはキュー内のデータを辛抱強く待機します。
実装に関しては、キューを共有メモリタイプのオブジェクトとして実行できますが、パイプはまさにあなたが探しているものだと思います。 CPUは必要に応じて単純にパイプに書き込みます。ハードディスク側では、パイプを読むだけで、有効なデータが得られたらそこから先に進みます。