3

私は非常に新しいプログラマーであり、Intelの例に問題があります。最も基本的な可能なループがtbbでどのように実装されているかを見ることができれば役立つと思います。

for (n=0 ; n < songinfo.frames; ++n) {  

         sli[n]=songin[n*2];
         sri[n]=songin[n*2+1];

}

これは、オーディオデータのインターリーブを解除するために使用しているループです。このループはtbbの恩恵を受けますか?どのように実装しますか?

4

1 に答える 1

7

まず最初に、次のコードについては、あなたarraysがタイプmytype*であると仮定します。そうでない場合、コードにいくつかの変更が必要です。さらに、範囲が重複していないと仮定します。そうしないと、並列化の試みが正しく機能しません(少なくとも追加の作業がなければ)

あなたがtbbでそれを求めたので:

まず、ライブラリをどこか(通常はのmain)で初期化する必要があります。using namespace tbbコードについては、どこかに置いたと仮定します。

int main(int argc, char *argv[]){
   task_scheduler_init init;
   ...
}

次に、配列をキャプチャしてforloopの本体を実行するファンクターが必要になります。

struct apply_func {
    const mytype* songin; //whatever type you are operating on
    mytype* sli;
    mytype* sri;
    apply_func(const mytype* sin, mytype* sl, mytype* sr):songin(sin), sli(sl), sri(sr)
    {}
    void operator()(const blocked_range<size_t>& range) {
      for(size_t n = range.begin(); n !=range.end(); ++n){
        sli[n]=songin[n*2];
        sri[n]=songin[n*2+1];
      }
    }
}

parallel_forこれで、このループを並列化するために使用できます。

size_t grainsize = 1000; //or whatever you decide on (testing required for best performance);
apply_func func(songin, sli, sri);
parallel_for(blocked_range<size_t>(0, songinfo.frames, grainsize), func);

それでうまくいくはずです(しばらくの間tbbを正しく見ていなかったので、小さな間違いがあるかもしれません)。c ++ 11を使用する場合は、次を使用してコードを簡略化できますlambda

size_t grainsize = 1000; //or whatever you decide on (testing required for best performance);
parallel_for(blocked_range<size_t>(0, songinfo.frames, grainsize), 
             [&](const blocked_range<size_t>&){
                for(size_t n = range.begin(); n !=range.end(); ++n){
                  sli[n]=songin[n*2];
                  sri[n]=songin[n*2+1];
                }
             });

とはいえ、tbbは、私が新しいプログラマーに推奨するものとはまったく異なります。スレッド化を非常にしっかりと把握するまで、並列化するのが簡単なコードのみを並列化することをお勧めします。このためにopenmp、多くのものを並列化するのに十分強力でありながら、tbbから始めるのが少し簡単な静かなものを使用することをお勧めします(ただし、それをサポートするコンパイラによって異なります)。ループの場合、次のようになります。

#pragma omp prallel for
for(size_t n = 0; n < songinfo.frames; ++n) {
  sli[n]=songin[n*2];
  sri[n]=songin[n*2+1];
}

次に、コンパイラーにコンパイルしてopenmp(-fopenmpgccの場合/openmpはvisual c ++の場合)とリンクするように指示する必要があります。ご覧のとおり、tbbよりも使用がかなり簡単で(このような簡単なユースケースの場合、より複雑なシナリオは別の問題です)、openmpまたはtbbもサポートしないプラットフォームで動作するという追加の利点があります(不明な#pragmasものは無視されるため)コンパイラによる)。個人的には、オープンソースライセンスを使用できず、tbbを購入するのはプロジェクトにとって少し急だったため、一部のプロジェクトではtbbを優先してopenmpを使用しています。

ループを邪魔にならないように麻痺させる方法がわかったので、それが価値があるかどうかという質問に取り掛かりましょう。これは、処理する要素の数とプログラムが実行されると予想されるプラットフォームの種類に完全に依存するため、実際には簡単に答えることができない質問です。あなたの問題は帯域幅が非常に重いので、パフォーマンスの大幅な向上は期待できません。

  • 要素のみを処理している場合1000、ループの並列バージョンは、オーバーヘッドのためにシングルスレッドバージョンよりも遅くなる可能性が非常に高くなります。
  • データがキャッシュになく(データが収まらないため)、システムの帯域幅が非常に不足している場合は、あまりメリットが見られない可能性があります(ただし、ある程度のメリットが見られる可能性はありますが、多数の1.Xプロセッサを使用している場合でも)
  • システムがccNUMAの場合(マルチソケットシステムの場合)、追加の転送コストのために、要素の量に関係なくパフォーマンスが低下する可能性があります
  • コンパイラーは、ポインターのエイリアシングに関する最適化を見逃す可能性があります(ループ本体が別の機能に移動されるため)。__restrict(gccの場合、vsの手がかりなし)を使用すると、その問題が解決する場合があります。
  • ..。

個人的には、パフォーマンスが大幅に向上する可能性が最も高いのは、システムに単一のマルチコアCPUがあり、データセットがL3-キャッシュ(個々のL2キャッシュではない)に収まる場合だと思います。より大きなデータセットの場合、パフォーマンスはおそらく向上しますが、それほどではありません(プリフェッチを正しく使用すると、同様の向上が得られる可能性があります)。もちろん、これは純粋な推測です。

于 2011-12-29T16:23:06.650 に答える