0

次のように疑似コードで記述できるアルゴリズムがあります。

for(int frame=0;frame <1000;frame++)
{
     Image *img=ReadFrame();
     mat processedImage=processImage(img);
     addtompeg(processedImage);
}

ProcessImage は時間がかかり、約 30 秒かかります。ReadFrame と AddToMpeg は遅くはありませんが、順番に実行する必要があります (そうしないと、フレーム 1 の前にフレーム 2 が出力に追加される可能性があります)。

OpenMP を使用して並列化するにはどうすればよいですか?

readframe と addtompeg に opencv を使用しています。

4

2 に答える 2

2

for技術的には、OpenMP では、プログラムが句を使用してシーケンシャルであるかのように、ループの一部を同じ順序で実行できますordered(セクション 2.8.7 を参照)。とにかく、次の 2 つの理由から、この句を使用することはお勧めしません。

  1. スレッドは、同じループ内で複数の順序付き領域を実行してはなりません(これはあなたのケースではないようです)
  2. 多くの実装では、orderedループはシーケンシャル ループのように動作し、パフォーマンスに悪影響を及ぼします。

したがって、あなたの場合に私が提案するのは、ループを展開することです:

Image * img           [chunk];
mat     processedImage[chunk];
/* ... */
for(int frame = 0; frame < nframes; frame += chunk) {

  #pragma omp single
  { /* Frames are read in sequential order */
    for( int ii = frame; ii < frame + chunk; ii++) {
       img[ii%chunk] = ReadFrame();
    }
  } /* Implicit barrier here */
  #pragma omp for
  for( int ii = frame; ii < frame + chunk; ii++) {
       processedImage[ii%chunk] = processImage(img[ii%chunk]); /* Images are processed in parallel */
  } /* Implicit barrier here */
  #pragma omp single
  { /* Frames are added to mpeg sequential order */
    for( int ii = frame; ii < frame + chunk; ii++) {
     addtompeg(processedImage[ii%chunk]);
    }
  } /* Implicit barrier here */
}

の値は、chunk主にメモリに関する考慮事項に依存します。メモリが問題にならないと思われる場合は、外側のループを完全に削除して、内側のループを から0に移動できnframesます。

もちろん、外側のループの残りの部分を正しく管理するように注意する必要があります (スニペットには示していません)。

于 2013-05-26T09:24:12.300 に答える
1

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 タスクが作成されます。imgframeは値によってキャプチャされます。つまり、です。したがって、がポインタの配列であるfirstprivate必要はありません。imgプロデューサー タスクはタスクに所有権を与えるimgため、タスクはイメージ オブジェクトの破棄を処理する必要があります。ここでは、各フレームを個別のバッファーに割り当て、毎回内部メモリを再利用しないことが重要ですReadFrame()(OpenCV を使用したことがなく、これが当てはまるかどうかはわかりません)。タスクはキューに入れられ、あるタスク スケジューリング ポイントで待機しているアイドル スレッドによって実行されます。コンストラクトの最後にある暗黙のバリアは、そのsingleようなスケジューリング ポイントであるため、残りのスレッドがタスクの実行を開始します。その度にnchunkフレームが読み取られるか、入力の最後に達した場合、プロデューサー スレッドは、キューに入れられたすべてのタスクが処理されるのを待ち (それが のtaskwait目的です)、チャンクを単に出力に書き込みます。

の適切な値を選択することnchunksが重要です。そうしないと、一部のスレッドがアイドル状態になる可能性があります。ReadFrameとが比較的高速である場合addtompeg、つまりフレームの読み取りと書き込みにnum_threadsかかる時間が よりも短い場合processImagenchunksはスレッド数の正確な倍数になります。さまざまな時間がかかる可能性がある場合は、負荷の不均衡を防ぐために、 にprocessImage非常に大きな値を設定する必要があります。nchunksこの場合processImage、代わりに並列化を試み、処理ループを直列に保ちます。

于 2013-05-26T12:38:32.240 に答える