Massimiliano のチャンキングのアイデアに基づいて構築された、より洗練されたソリューションは、OpenMP 3.0 以降の明示的なタスク メカニズムを使用することです (つまり、Visual Studio の C++ コンパイラでは機能しません)。
const int nchunks = 10;
#pragma omp parallel
{
#pragma omp single
{
mat processedImage[nchunks];
for (int frame = 0; frame < nframes; frame++)
{
Image *img = ReadFrame();
#pragma omp task shared(processedImage)
{
processedImage[frame % nchunks] = processImage(img);
disposeImage(img);
}
// nchunks frames read or the last frame reached
if ((1 + frame) % nchunks == 0 || frame == nframes-1)
{
#pragma omp taskwait
int chunks = 1 + frame % nchunks;
for (int i = 0; i < chunks; i++)
addtompeg(processedImage[i]);
}
}
}
}
コードはぎこちなく見えるかもしれませんが、概念的には非常に単純です。OpenMP コンストラクトがなければnchunks
、出力 MPEG ファイルにループで追加する前に、処理されたフレームをバッファリングするシリアル コードのようなものになります。このコード ブロックで魔法が起こります。
#pragma omp task shared(processedImage)
{
processedImage[frame % nchunks] = processImage(img);
disposeImage(img);
}
これにより、ブロック内の 2 行のコードを実行する新しい OpenMP タスクが作成されます。img
とframe
は値によってキャプチャされます。つまり、です。したがって、がポインタの配列であるfirstprivate
必要はありません。img
プロデューサー タスクはタスクに所有権を与えるimg
ため、タスクはイメージ オブジェクトの破棄を処理する必要があります。ここでは、各フレームを個別のバッファーに割り当て、毎回内部メモリを再利用しないことが重要ですReadFrame()
(OpenCV を使用したことがなく、これが当てはまるかどうかはわかりません)。タスクはキューに入れられ、あるタスク スケジューリング ポイントで待機しているアイドル スレッドによって実行されます。コンストラクトの最後にある暗黙のバリアは、そのsingle
ようなスケジューリング ポイントであるため、残りのスレッドがタスクの実行を開始します。その度にnchunk
フレームが読み取られるか、入力の最後に達した場合、プロデューサー スレッドは、キューに入れられたすべてのタスクが処理されるのを待ち (それが のtaskwait
目的です)、チャンクを単に出力に書き込みます。
の適切な値を選択することnchunks
が重要です。そうしないと、一部のスレッドがアイドル状態になる可能性があります。ReadFrame
とが比較的高速である場合addtompeg
、つまりフレームの読み取りと書き込みにnum_threads
かかる時間が よりも短い場合processImage
、nchunks
はスレッド数の正確な倍数になります。さまざまな時間がかかる可能性がある場合は、負荷の不均衡を防ぐために、 にprocessImage
非常に大きな値を設定する必要があります。nchunks
この場合processImage
、代わりに並列化を試み、処理ループを直列に保ちます。