場合によっては、ループが複雑な制御フローを持つことがあります。このような状況で 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 プラグマやディレクティブを記述する必要はありません。