同期化構造とは、共有データの一貫性を保証し、スレッド間の並列実行を調整するのに使用されます。
同期化構造のディレクティブは次のとおりです:
ATOMIC ディレクティブ
BARRIER ディレクティブ
CRITICAL ディレクティブ
FLUSH ディレクティブ
MASTER ディレクティブ
ORDERED ディレクティブ
ATOMIC ディレクティブを使用して、特定のメモリ・ロケーションをアトミックに更新し、同時に複数のスレッドによる書き込みの危険性を回避するようにします。
このディレクティブは、直後に続く文にのみ適用され、次の形式のいずれかでなければなりません:
x = x operator expr
x = expr operator x
x = intrinsic (x, expr)
x = intrinsic (expr, x)
上記の文について、次に説明します。
x は、組込み型のスカラ変数です。
expr は、x を参照しないスカラ式です。
intrinsic は、MAX、MIN、IAND、IOR、または IEOR のいずれかです。
operator は、+、*、-、/、.AND.、.OR.、.EQV. または .NEQV. のいずれかです。
このディレクティブは、代入に関して、クリティカル・セクションで行われる最適化以上の最適化を可能にします。クリティカル・セクションで文を囲むことによって、すべての ATOMIC ディレクティブを置換することができます。これらすべてのクリティカル・セクションは、同一で一意な名前を使用する必要があります。
x のロードとストアのみがアトミックです。expr の評価は、アトミックではありません。競合状態を避けるには、競合状態ではないことがわかっているロケーションを除いて、そのロケーションのすべての並列の更新において、ATOMIC ディレクティブを使用して保護する必要があります。intrinsic 関数、operator 演算子、および代入は、組込みの関数、演算子および代入でなくてはなりません。
次の制限は、ATOMIC ディレクティブに適用されます: ストレージ・ロケーション x へのすべての参照は、同じ型のパラメータでなければなりません。
次の例では、Y のロケーションの集合は、アトミックに更新されます。
!$OMP ATOMIC
Y = Y + B(I)
並列領域内ですべてのスレッドを同期化するには、BARRIER ディレクティブを使用します。このディレクティブは、PARALLEL ディレクティブにより定義される並列領域内でのみ使用できます。BARRIER ディレクティブは、DO、PARALLEL DO、SECTIONS、PARALLEL SECTIONS、SINGLE ディレクティブ内では使用できません。
各スレッドは、すべてのスレッドがディレクティブに到達するまで BARRIER ディレクティブで待機します。
次の例では、BARRIER ディレクティブは、すべてのスレッドが最初のループを実行し、2 番目のループの実行が安全に行われることを保証します。
c$OMP PARALLEL
c$OMP DO PRIVATE(i)
DO i = 1, 100
b(i) = i
END DO
c$OMP BARRIER
c$OMP DO PRIVATE(i)
DO i = 1, 100
a(i) = b(101-i)
END DO
c$OMP END PARALLEL
CRITICAL および END CRITICAL ディレクティブを使用して、クリティカル・セクションとして参照されるコードブロックへのアクセスを、一度に 1 つのスレッドに制限します。
スレッドは、同じ名前を持つクリティカル・セクションを実行するチーム内の他のスレッドがなくなるまで、クリティカル・セクションの最初で待機します。
スレッドが、クリティカル・セクションに入ると、ラッチ変数がクローズに設定され、他のスレッドは、ロックアウトされます。そのスレッドが、END CRITICAL ディレクティブでクリティカル・セクションから出ると、ラッチ変数は、オープンに設定され、別のスレッドがクリティカル・セクションにアクセスできるようになります。
クリティカル・セクションの名前を CRITICAL ディレクティブで指定した場合、END CRITICAL ディレクティブでも同じ名前を指定する必要があります。CRITICAL ディレクティブの名前を指定しない場合、END CRITICAL ディレクティブの名前を指定することはできません。
名前を指定されていないすべての CRITICAL ディレクティブは、同じ名前にマップします。クリティカル・セクションの名前は、プログラムに対してグローバルです。
次の例では、複数の CRITICAL ディレクティブが含まれ、1 つのタスクがキューから取り出されて処理されるキューイングのモデルを示しています。複数のスレッドが同じタスクをキューから取り出さないために、デキューの操作はクリティカル・セクションになければなりません。この例では、2 つの独立したキューがあるため、各キューは、それぞれ X_AXIS と Y_AXIS という異なる名前を持つ CRITICAL ディレクティブによって保護されます。
!$OMP PARALLEL DEFAULT(PRIVATE,SHARED(X,Y)名前を指定されていないクリティカル・セクションは、Pthread パッケージからグローバル・ロックを使用します。これにより、同じロックを使用して他のコードと同期することができます。名前の付いたロックは、コンパイラにより作成され保守されて、さらに効率的です。
FLUSH ディレクティブを使用して、メモリの一貫性のあるビューが提供される同期ポイントを識別します。スレッドから見える変数は、このポイントでメモリに書き戻されます。
このポイントでスレッドから見えるすべての変数のフラッシュを避けるには、フラッシュする名前付きの変数をカンマ区切りのリストに含めます。
次の例では、FLUSH ディレクティブを使用して、変数 ISYNC のスレッド 0 とスレッド 1 の Point To Point (ポイント間) の同期化を示します。
!$OMP PARALLEL DEFAULT(PRIVATE),SHARED(ISYNC)
IAM = OMP_GET_THREAD_NUM()
ISYNC(IAM) = 0
!$OMP BARRIER
CALL WORK()
!I Am Done With My Work, Synchronize With My Neighbor
ISYNC(IAM) = 1
!$OMP FLUSH(ISYNC)
!Wait Till Neighbor Is Done
DO WHILE (ISYNC(NEIGH) .EQ.0)
!$OMP FLUSH(ISYNC)
END DO
!$OMP END PARALLEL
MASTER および END MASTER ディレクティブを使用して、マスタスレッドのみが実行するコードブロックを識別します。
チームの他のスレッドは、コードをスキップして実行を続けます。END MASTER ディレクティブには、暗黙的なバリアはありません。
次の例では、マスタスレッドのみが OUTPUT と INPUT ルーチンを実行します。
!$OMP PARALLEL DEFAULT(SHARED)
CALL WORK(X)
!$OMP MASTER
CALL OUTPUT(X)
CALL INPUT(Y)
!$OMP END MASTER
CALL WORK(Y)
!$OMP END PARALLEL
ORDERED および END ORDERED ディレクティブを DO 構造内で使用すると、オーダード (ordered) セクション内では処理をシーケンシャルに実行し、セクション外では、処理を並列に実行することができます。
ORDERED ディレクティブを使用する場合は、DO ディレクティブでも ORDERED 節を指定する必要があります。
オーダード・セクションには、一度に 1 つのスレッドのみがループの繰返し順序で入ることができます。
次の例では、インデックスをシーケンシャルにプリントアウトします。
!$OMP DO ORDERED,SCHEDULE(DYNAMIC)
DO I=LB,UB,ST
CALL WORK(I)
END DO
SUBROUTINE WORK(K)
!$OMP ORDERED
WRITE(*,*) K
!$OMP END ORDERED