11. Memory Model#

OpenMP provides a shared-memory model that allows all threads on a given device shared access to memory. For a given OpenMP region that may be executed by more than one thread or SIMD lane, variables in memory may be shared or private with respect to those threads or SIMD lanes. A variable’s data-sharing attribute indicates whether it is shared (the shared attribute) or private (the private, firstprivate, lastprivate, linear, and reduction attributes) in the data environment of an OpenMP region. While private variables in an OpenMP region are new copies of the original variable (with same name) that may then be concurrently accessed or modified by their respective threads or SIMD lanes, a shared variable in an OpenMP region is the same as the variable of the same name in the enclosing region. Concurrent accesses or modifications to a shared variable may therefore require synchronization to avoid data races.

OpenMP’s memory model also includes a temporary view of memory that is associated with each thread. Two different threads may see different values for a given variable in their respective temporary views. Threads may employ flush operations for the purposes of making their temporary view of a variable consistent with the value of the variable in memory. The effect of a given flush operation is characterized by its flush properties - some combination of strong, release, and acquire - and, for strong flushes, a flush-set.

A strong flush will force consistency between the temporary view and the memory for all variables in its flush-set. Furthermore, all strong flushes in a program that have intersecting flush-sets will execute in some total order, and within a thread strong flushes may not be reordered with respect to other memory operations on variables in its flush-set. Release and acquire flushes operate in pairs. A release flush may “synchronize’’ with an acquire flush, and when it does so the local memory operations that precede the release flush will appear to have been completed before the local memory operations on the same variables that follow the acquire flush.

Flush operations arise from explicit flush directives, implicit flush directives, and also from the execution of atomic constructs. The flush directive forces a consistent view of local variables of the thread executing the flush. When a list is supplied on the directive, only the items (variables) in the list are guaranteed to be flushed. Implied flushes exist at prescribed locations of certain constructs. For the complete list of these locations and associated constructs, please refer to the flush Construct section of the OpenMP Specifications document.

In this chapter, examples illustrate how race conditions may arise for accesses to variables with a shared data-sharing attribute when flush operations are not properly employed. A race condition can exist when two or more threads are involved in accessing a variable and at least one of the accesses modifies the variable. In particular, a data race will arise when conflicting accesses do not have a well-defined completion order. The existence of data races in OpenMP programs result in undefined behavior, and so they should generally be avoided for programs to be correct. The completion order of accesses to a shared variable is guaranteed in OpenMP through a set of memory consistency rules that are described in the OpenMP Memory Consistency section of the OpenMP Specifications document.