並列サイト・アノテーションは、並列サイトの開始と終了をマークします。同様に、並列タスク・アノテーションは、それぞれのタスク領域の開始と終了をマークします。並列サイトに複数のタスクが含まれる場合、タスクコードにループ本体全体が含まれない場合、あるいは複数のタスク終了アノテーションを含む特定のタスクの開始/終了境界を必要とする複雑なループやコードの場合、このタスクの開始と終了アノテーションのペアを使用します。
並列サイト・アノテーションは並列サイトをマークします。
C/C++: |
ANNOTATE_SITE_BEGIN(sitename);とANNOTATE_SITE_END(); |
Fortran: |
call annotate_site_begin(sitename)とcall annotate_site_end |
C#: |
Annotate.SiteBegin(sitename);とAnnotate.SiteEnd(); |
並列サイト内の各タスクをマークする並列タスク・サイトアノテーションです。
C/C++: |
ANNOTATE_TASK_BEGIN(taskname);とANNOTATE_TASK_END(); |
Fortran: |
call annotate_task_begin(taskname)とcall annotate_task_end |
C#: |
Annotate.TaskBegin(taskname);とAnnotate.TaskEnd(); |
C/C++ では、ANNOTATE_TASK_END();アノテーションのtaskname引数はオプションです。
taskname は、アノテーション名の引数規則に従います。
C/C++ コードでは、taskname は ASCII C++ 識別子です。インテル® Advisor のツールレポートに表示したときに分かりやすい名前を選択すると良いでしょう。
Fortran コードでは、taskname は文字定数です。インテル® Advisor のツールレポートに表示したときに分かりやすい名前を選択すると良いでしょう。
C# コードでは、taskname は文字列です。インテル® Advisor のツールレポートに表示したときに分かりやすい文字列を選択すると良いでしょう。
単一のタスクの単純なループのサイトとタスク・アノテーションをこの一般的な複数のタスクを持つ形式に変換する場合、タスク領域をマークするタスクの開始とタスクの終了アノテーションで単一の反復ループ・アノテーションを置き換えます。両方の形式は同じ並列サイト・アノテーションを使用します。
C++ のサンプル・アプリケーション stats は、並列サイトにあるループではない複数のタスクによるタスク並列処理を示します。この場合、いくつかの関連する文が多くの計算を行い、それぞれを個別のタスクにすることができます。
ANNOTATE_SITE_BEGIN(MySite1);
cout << "Start calculating running average..."<<endl;
ANNNOTATE_TASK_BEGIN(MyTask1);
runningAvg(vals, SIZE, rnAvg);
ANNOTATE_TASK_END(MyTask1);
cout << "Start calculating running standard deviation..."<<endl;
ANNOTATE_TASK_BEGIN(MyTask2);
runningStdDev(vals, SIZE, rnStdDev);
ANNOTATE_TASK_END(MyTask2);
ANNOTATE_SITE_END(MySite1);サーベイツールは、計算を行う関数の呼び出しに加えて、多くの CPU 時間を消費する単一文を示すことがあります。例えば、大規模な Fortran 配列への代入などがあります。
次の C/C++ コードのアノテーションは、それぞれのループ反復が 2 つのタスクであり、任意の反復やほかのタスクと並列に実行される可能性があることを示します。
...
ANNOTATE_SITE_BEGIN(sitename);
for (I=0; i<N; I++) {
ANNOTATE_TASK_BEGIN(task1);
func1(I);
ANNOTATE_TASK_END();
ANNOTATE_TASK_BEGIN(task2);
func2(I);
ANNOTATE_TASK_END();
}
ANNOTATE_SITE_END();
..次の Fortran コードの Fortran サイトとタスクのアノテーションは、それぞれのループ反復が 2 つのタスクであり、任意の反復やほかのタスクと並列に実行される可能性があることを示します。
...
call annotate_site_begin("sitename")
do i=1,size
call annotate_task_begin("task1")
call func1(i)
call annotate_task_end
call annotate_task_begin("task2")
call func2(i)
call annotate_task_end
end do
call annotate_site_end
...次の C# コードの C# サイトとタスクのアノテーションは、それぞれのループ反復が 2 つのタスクであり、任意の反復やほかのタスクと並列に実行される可能性があることを示します。
...
Annotate.SiteBegin("sitename");
for (int i = 0; i < N; i++) {
Annotate.TaskBegin("task1");
func1(i);
Annotate.TaskEnd();
Annotate.TaskBegin("task2");
func2(i);
Annotate.TaskEnd();
}
Annotate.SiteEnd();
...それぞれのタスクコードは、並列サイト内で task begin と task end アノテーション・ペアの間にマークされます。タスクで実行されないコードは、サイトに入るスレッドによって実行されますが、これは識別されたタスクと並列に実行される可能性があります。この例では、i のインクリメント、i と N の比較を行うループの制御コードは、明示的に指定したタスクとは別に実行されます。これは、タスク間、そしてタスク外部のコードとの競合の可能性を意味します。
上記のコードで依存関係ツールを使用すると、以降のループ反復で func1 または func2 によってアクセスされるグローバルデータのデータ競合がレポートされる可能性があります。
ヘルプトピックの「並列サイトとタスクのアノテーション」は、並列サイトとタスクの追加を説明しています。
次のような C/C++ コードについて考えてみます。
...
ANNOTATE_SITE_BEGIN(sitename);
for (i=0; i<N; i++) {
ANNOTATE_ITERATION_TASK(taskname);
func(i);
}
ANNOTATE_SITE_END();
... |
...
...
for (i=0; i<N; i++) {
ANNOTATE_SITE_BEGIN(sitename);
ANNOTATE_TASK_BEGIN(taskfunc1);
func1(i);
ANNOTATE_TASK_END();
ANNOTATE_TASK_BEGIN(taskfunc2);
func2(i);
ANNOTATE_TASK_END();
ANNOTATE_SITE_END();
}
... |
左の簡単な例では、単一のアノテーションがループ全体を囲んでいます。これにより、ループのすべての反復は潜在的に同時に実行されます。可能であればループにはこの単純な形式のアノテーション (2 つのサイト・アノテーションと 1 つの反復タスク・アノテーション) を使用します。
右の例では、すべてのループ反復が並列実行されるように指定するのではなく、ループ の単一の反復内にのみ並列処理を適用するよう指定しています。この場合、一度に 1 つのループ反復からの func1 と func2 の呼び出しのみを並列処理することができます。そのため右側の例では、並列に実行することを意図していないため、func1 の連続した呼び出しで競合は発生しません。
以下は、これら 2 つのケースを並列化するモデルを比較した図です。処理は左から右に時系列で示します。

垂直に並んだボックスは、並列実行のモデル化を示します。
ANNOTATE_TASK_BEGIN(taskname) と ANNOTATE_TASK_END() ペアの実行でタスクの動的範囲を指定します。タスク間の相互作用を収集するため、インテル® Advisor の依存関係またはスータビリティー解析中にアノテーションが実行されたびに、最も密接な動的サイトに関連付けられた動的範囲が識別されます。それぞれのタスクは独立しており、タスクが含まれるサイト内のすべてのタスクと並列に実行できると仮定されます。
複数のタスクを持つ並列サイト内のタスク・アノテーションは、次の規則に従って使用しなければなりません。
実行パスに従って、各タスク開始アノテーションはタスク終了アノテーションで終了しなければなりません。
タスク境界は並列サイト境界内になければなりません。
タスク・アノテーションの引数 は、アノテーション名の引数規則に従います。
タスクが並列実行がモデル化されないのは、次の場合です。
タスクが同期を使用するとき、同期領域内で指定されたコードは、同じロックアドレスを使用して同期される他のコードと並列にはモデル化されません。
タスクが別のタスクを生成すると、2 番目のタスクが生成される前に実行された親タスクのコードは、タスクの生成前に実行されるとみなされます。しかし、タスクの生成後に実行されるコードは、入れ子のタスクと並列であると仮定されます。以下に例を示します。
...
ANNOTATE_SITE_BEGIN(sitename);
for (i=0; i<N; i++) {
ANNOTATE_TASK_BEGIN(taskfunc1a);
func1a(i);
ANNOTATE_TASK_BEGIN(taskfunc1a);
func2(i);
ANNOTATE_TASK_END();
func1b(i);
ANNOTATE_TASK_END();
}
ANNOTATE_SITE_END();
...この例では、func1a(i) は func2(i) または func1b(i) と並列ではありません。しかし、func2(i) と func1b(i) は、並列実行できるようにモデル化されています。このセマンティクスの解釈は、入れ子になった呼び出しが並列実行されるタスクを生成する再帰のモデル化を可能にします。この例では、単一の反復内のタスクではこの並列関係が成り立ちますが、異なるループ反復のタスクとは特別な関係がないためすべて並列になります。例えば、あるループ反復からの func1a(i) は、異なる反復の func2(i) と同時に実行される可能性があります。
依存関係の確認では、依存関係ツールは明示的な同期が使用されない限り、特定のサイト内のすべてのタスクは並列実行されると仮定します。例えばこの例では、func1 と func2 の反復 N はすべて並列に実行されます。
...
ANNOTATE_SITE_BEGIN(sitename);
for (I=0; i<N; I++) {
ANNOTATE_TASK_BEGIN(taskfunc1);
func1(I);
ANNOTATE_TASK_END();
ANNOTATE_TASK_BEGIN(taskfunc2);
func2(I);
ANNOTATE_TASK_END();
}
ANNOTATE_SITE_END();
...その他の関係をモデル化する場合、例えば、func2 の呼び出しに何らかのシリアル化がある場合、タスクの実行中に取得および解放されるロックをマークするロック・アノテーションを使用してそれを明示的に表現する必要があります。
タスク・アノテーションを追加する場所を選択するには、平均インスタンス時間や反復数 (スータビリティー・レポートで提供される) などの要因を考慮して、いくつかの検証を行います。並列サイトのループが入れ子構造で最も内側のループの計算時間が少ない場合、その 1 つ外側のループ周辺にタスク・アノテーションを追加することを検討してください。「タスクの適度な大きさは?」などのヘルプトピックをご覧ください。