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、代わりに並列化を試み、処理ループを直列に保ちます。