場合によっては、ループが複雑な制御フローを持つことがあります。このような状況で OpenMP* を使用する場合、単純なループを扱う場合よりも多くのことが求められます。タスク本体では、アノテーション・サイト内で定義されている自動変数にアクセスしてはなりません。このような変数は、タスクの実行前または実行中に破棄される可能性があります。また、task 構造で参照される変数はデフォルトで firstprivate であることに注意してください。
C/C++ コードについて考えてみます。
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();
ここでは、OpenMP* の task プラグマを使用します。そうでないと、このようなループの並列化はかなり困難になります。ループを並列化する 1 つの手法として、DoSomething() 呼び出しを単純にスポーンする方法があります。
extern char a[]; int previousEnd = -1; #pragma omp parallel { #pragma omp single { ... for (int i=0; i<=100; i++) { if (!a[i] || i==100) { #pragma omp task DoSomething(previousEnd+1,i); } } } }
ここで、DoSomething への引数は参照渡しではなく、値渡しであることが重要です。それは、previousEnd と i が、スポーンされたタスクの実行前または実行中に変更される可能性があるためです。
次の Fortran コードついて考えてみます。
... logical(1) a(200) integer(4) i, previousEnd ... previousEnd=0 call annotate_site_begin(functions) do i=1,101 if a(.not. a(i)) .or.(i .eq.101) then call annotate_task_begin(do_something) call DoSomething(previousEnd+1, i) call annotate_task_end endif end do call annotate_site_end
ここでは OpenMP* の task ディレクティブを使用します。そうでないと、このようなループの並列化はかなり困難となるでしょう。上記のループを並列化する 1 つの手法として、DoSomething() 呼び出しを単純にスポーンする方法があります。
... logical(1) a(200) integer(4) i, previousEnd ... previousEnd=0 !$omp parallel !$omp single do i=1,101 if a(.not. a(i)) .or.(i .eq.101) then !$omp task call DoSomething(previousEnd+1, i) !$omp end task endif end do !$omp end parallel
parallel ディレクティブで囲まれた静的範囲内では、omp task プラグマやディレクティブを記述する必要はありません。