Visible to Intel only — GUID: GUID-DA91BD4B-3AB8-4927-9385-5CEEEFF72496
Visible to Intel only — GUID: GUID-DA91BD4B-3AB8-4927-9385-5CEEEFF72496
ATOMIC
OpenMP* Fortran Compiler Directive: Ensures that a specific memory location is updated atomically. This prevents the possibility of multiple threads simultaneously reading and writing the specific memory location.
!$OMP ATOMIC [clause[[[,] clause]...]]
block
[!$OMP END ATOMIC]
clause |
(Optional) Is one of the following:
|
block |
Is one of the following:
|
statement |
Is one of the following:
|
capture-statement |
Is an expression in the form v = x. |
compare-statement |
Is as follows: if (x == e) then x = d end or: if (x == e) x = d or if CAPTURE also appears and block contains no capture-statement it can also be the following: if (x == e) then x = d else v = x end if |
update-statement |
Is an expression with one of the following forms: x = x operator expr x = expr operator x x = intrinsic (x, expr-list) x = intrinsic (expr-list, x) The following rules apply:
|
write-statement |
Is an expression in the form x = expr. |
d, e, x, v |
Are scalar variables of intrinsic type. During execution of an atomic region, all references to storage location x must specify the same storage location. v must not access the same storage location as x. |
expr, expr-list |
expr is a scalar expression. expr-list is a comma-separated list of expressions. They must not access the same storage location as x or v. If intrinsic is IAND, IOR, or IEOR, then expr-list can contain only one expression. |
operator |
Is one of the following intrinsic operators: +, *, -, /, ,AND., ,OR., .EQV., or .NEQV.. |
intrinsic |
Is one of the following intrinsic procedures: MAX, MIN, IAND, IOR, or IEOR. |
If x is of size 8, 16, 32, or 64 bits and x is aligned to a multiple of its size, the binding thread set is all threads on the device. Otherwise, the binding thread set is all threads in the contention group. Atomic regions enforce exclusive access with respect to other atomic regions that access the same storage location x among all the threads in the binding thread set without regard to the teams to which the threads belong.
If !$OMP ATOMIC is specified with no atomic-clause, it is the same as specifying !$OMP ATOMIC UPDATE.
If !$OMP ATOMIC CAPTURE is specified, you must include an !$OMP END ATOMIC directive following the block. Otherwise, the !$OMP END ATOMIC directive is optional.
Note that the following restriction applies to the ATOMIC directive:
All atomic accesses to the storage locations designated by x throughout the program must have the same type and type parameters.
The following table describes what happens when you specify one of the values in the atomic-clause in an ATOMIC construct.
Clause | Result |
---|---|
READ |
Causes an atomic read of the location designated by x regardless of the native machine word size. |
UPDATE |
Causes an atomic update of the location designated by x using the designated operator or intrinsic. The following rules also apply:
|
WRITE |
Causes an atomic write of the location designated by x regardless of the native machine word size. |
If all of the following conditions are true, the strong flush on entry to the atomic operation is also a RELEASE flush:
The atomic-clause is WRITE or UPDATE.
The atomic operation is not a conditional update for which the comparison fails.
The effective memory ordering is RELEASE, ACQ_REL, or SEQ_CST.
If both of the following conditions are true, the strong flush on exit from the atomic operation is also an ACQUIRE flush:
The atomic-clause is READ or UPDATE.
The effective memory ordering is ACQUIRE, ACQ_REL, or SEQ_CST.
Therefore, as the above shows, the effective memory ordering is not RELAXED. RELEASE and ACQUIRE flushes can be implied and permit synchronization between threads without an explicit FLUSH directive.
Any combination of two or more of these atomic constructs enforces mutually exclusive access to the locations designated by x.
A race condition exists when two unsynchronized threads access the same shared variable with at least one thread modifying the variable; this can cause unpredictable results. To avoid race conditions, all accesses of the locations designated by x that could potentially occur in parallel must be protected with an ATOMIC construct.
Atomic regions do not guarantee exclusive access with respect to any accesses outside of atomic regions to the same storage location x even if those accesses occur during a CRITICAL or ORDERED region, while an OpenMP* lock is owned by the executing task, or during the execution of a REDUCTION clause.
However, other OpenMP* synchronization can ensure the desired exclusive access. For example, a BARRIER directive following a series of atomic updates to x guarantees that subsequent accesses do not form a race condition with the atomic accesses.
The following example shows a way to avoid race conditions by using ATOMIC to protect all simultaneous updates of the location by multiple threads.
Since the ATOMIC directive below applies only to the statement immediately following it, elements of Y are not updated atomically.
REAL FUNCTION FOO1(I)
INTEGER I
FOO1 = 1.0 * I
RETURN
END FUNCTION FOO1
REAL FUNCTION FOO2(I)
INTEGER I
FOO2 = 2.0 * I
RETURN
END FUNCTION FOO2
SUBROUTINE SUB(X, Y, INDEX, N)
REAL X(*), Y(*)
INTEGER INDEX(*), N
INTEGER I
!$OMP PARALLEL DO SHARED(X, Y, INDEX, N)
DO I=1,N
!$OMP ATOMIC UPDATE
X(INDEX(I)) = X(INDEX(I)) + FOO1(I)
Y(I) = Y(I) + FOO2(I)
ENDDO
END SUBROUTINE SUB
PROGRAM ATOMIC_DEMO
REAL X(1000), Y(10000)
INTEGER INDEX(10000)
INTEGER I
DO I=1,10000
INDEX(I) = MOD(I, 1000) + 1
Y(I) = 0.0
ENDDO
DO I = 1,1000
X(I) = 0.0
ENDDO
CALL SUB(X, Y, INDEX, 10000)
END PROGRAM ATOMIC_DEMO
The following non-conforming example demonstrates the restriction on the ATOMIC construct:
SUBROUTINE ATOMIC_INCORRECT()
INTEGER:: I
REAL:: R
EQUIVALENCE(I,R)
!$OMP PARALLEL
!$OMP ATOMIC UPDATE
I = I + 1
!$OMP ATOMIC UPDATE
R = R + 1.0
! The above is incorrect because I and R reference the same location
! but have different types
!$OMP END PARALLEL
END SUBROUTINE ATOMIC_INCORRECT