ロック・アノテーションを使用して、明示的な同期処理を追加する場所をマークします。
C/C++: |
ANNOTATE_LOCK_ACQUIRE(pointer-expression); と ANNOTATE_LOCK_RELEASE(pointer-expression); |
Fortran: |
call annotate_lock_acquire(address) と call annotate_lock_release(address) |
C#: |
Annotate.LockAcquire([int expr]); と Annotate.LockRelease([int expr]); (アノテーションごと、引数はオプション) |
C/C++ と Fortran プログラムでは、すべてのロック・アノテーションは最終的なプログラムの個々のロックを示すアドレス値を使用します。また、アドレス値 0 は、プログラム全体のグローバル ロックを表します。C# プログラムでは、引数はintでデフォルト値は 0 (ゼロ) です。
パフォーマンスをスケーリングするため追加ロックを必要としないならば、デフォルトのロックから始めることを推奨します。
モデル化のステップでは、インテル® oneAPI スレッディング・ビルディング・ブロック (oneTBB) および OpenMP* に加えて、Windows* API の標準ロックルーチンを認識するため、既存のロックをアノテートする必要はありません。ロック・アノテーションは、同期を使用していない場合にのみ必要です。
ロック取得とロック解放アノテーションは、実際にロックの取得と解放を意図するプログラムの場所を示します。これらのアノテーションは、指定するアドレスを示す単一の引数を持ちます。
例えば、glob_variable にのみロックを使用する場合、glob_variable へのアクセスを保護するすべてのケースで同じアドレスを指定して、特定のロックを表現します。次のサンプルは、変数のアドレスを使用して glob_variable のロックを示します。
通常、次の 4 つの値を使用できますが、必要であればさらに細かな粒度の同期を使用します。
値 0 (ゼロ) は、プログラム全体で未指定の同じロックを表現します。
データ構造体またはほかのデータの集合のアドレス。これは、データの集合に単一のロックを使用することを表します。
データ集合のメンバーのアドレス。これは、データ構造体またはほかのデータ集合のアドレスを指定する場合よりも粒度の細かいロックを使用して、パフォーマンスを向上します。
(最終的な並列コードに近づいたら) ロックを示す変数。
この C/C++ の例は、並列プログラムの各タスクがグローバル変数 glob_variable にアクセスする近辺で、ロックを取得して解放することを意図しています。
... ... extern int glob_variable = 0; ... ANNOTATE_SITE_BEGIN(sitename); for (i=0; i<N; i++) { ANNOTATE_TASK_BEGIN(taskfunc1); func1(i); ANNOTATE_LOCK_ACQUIRE(&glob_variable); glob_variable++; ANNOTATE_LOCK_RELEASE(&glob_variable); func2(i); ANNOTATE_TASK_END(); } ANNOTATE_SITE_END(); ...
この Fortran の例は、並列プログラムの各タスクがグローバル変数 glob_variable にアクセスする近辺で、ロックを取得して解放することを意図しています。
... integer :: glob_variable = 0 call annotate_site_begin("sitename") do i=1,size call annotate_task_begin("taskfunc1") call func1(i) call annotate_lock_acquire(0) glob_variable = glob_variable + 1 call annotate_lock_release(0) call func2(i) call annotate_task_end end do call annotate_site_end ...
この C# の例は、並列プログラムの各タスクがグローバル変数 glob_variable にアクセスする近辺で、ロックを取得して解放することを意図しています。
... public int glob_variable { get{return nrOfSolutions;} set{nrOfSolutions = value;} } Annotate.SiteBegin("sitename"); for (int i = 0; i < N; i++) { Annotate.TaskBegin("taskfunc1"); func1(i); Annotate.LockAcquire(); glob_variable++; Annotate.LockRelease(); func2(i); Annotate.TaskEnd(); } Annotate.SiteEnd(); ...
次の C/C++ プログラムは、データ項目のアドレスを使用する典型的な例です。これは、Entity アドレスの使い方を示しています。ここでは、プログラムが異なるタスクによってアクセスされる配列のランダムな要素をカウントしており、その一部は同じランダム値を持っているため、それぞれの整数ベクトルが関連するロックを持ちます。アノテーションの追加は、太字で示します。
struct Entity { int val; }; ... std::vector<Entity> v; ... for (int i=0; i<v.size()*10000; i++) { int random_int = random_n(); ANNOTATE_LOCK_ACQUIRE(&v[random_int]); v[random_int].val++; ANNOTATE_LOCK_RELEASE(&v[random_int]); } ...
ロックアドレスは、ロック・アノテーションの基本であり、それぞれのロックアドレスは、最終的なプログラムで作成する一意なロックまたはその他の同期メカニズムに対応します。並列サイトを共有するタスクは、ロックアドレスまたは既存のロックメカニズムを使用して記述しない限り、並列実行としてモデル化されます。