データの並列処理 - インテル® oneAPI スレッディング・ビルディング・ブロック (oneTBB) の複雑な反復制御のループ

ループが複雑な制御フローを持つことがあります。このような状況でインテル® oneAPI スレッディング・ビルディング・ブロック (oneTBB) を使用する場合、単純なループを扱う場合よりも多くのことが求められます。タスク本体では、アノテーション・サイト内で定義されている自動変数にアクセスしてはなりません。このような変数は、タスクの実行前または実行中に破棄される可能性があるためです。次のシリアルコードについて考えてみます。

 extern char a[];
 int previousEnd = -1;
  ANNOTATE_SITE_BEGIN(sitename);
    for (int i=0; i<=100; i++) {
       if (!a[i] || i==100) {
          ANNOTATE_TASK_BEGIN(do_something);
              DoSomething(previousEnd+1,i);
          ANNOTATE_TASK_END();
          previousEnd=i;
       }
    }
 ANNOTATE_SITE_END();

複雑な制御を持つループは本質的にシーケンシャルであるため、一般に単純なカウント付きループのほうが複雑な反復制御を持つループよりもスケーラビリティーが優れています。可能であれば、単純なカウント付きのループへの書き換えを検討してください。

上記のループを並列化する 1 つの手法として、oneTBB の task_group 機能を使用することができます。

 
 #include <tbb/tbb.h>
 ...
 extern char a[]; 
 int previousEnd = -1; 
 task_group g;
    for (int i=0; i<=100; i++) {
        if (!a[i] || i==100) {
            g.run([=]{DoSomething(previousEnd+1,i);}
            previousEnd=i;
        }
    }
  g.wait(); // グループ内のすべてのタスクの完了を待機
 

式がファンクターを構成する場合、ipreviousEnd の値を取得することが重要であるため、値によってキャプチャーするラムダ式 [=] を使用します。これは、previousEndi の値が後で変化するためです。

tbb::task_group に関する詳細は、oneTBB のドキュメントを参照してください。

関連情報