Critical Sections and Atomic Operations
Critical sections are blocking; no thread can enter a critical section until the thread executing the block has exited. It is illegal to jump into or out of a critical section; the code block must be executed in its entirely by all threads.
It is possible to use named critical sections to partially minimize the serialization, but this is beyond our scope.
Critical sections can surround any block of code of any length. However, a frequent pattern is the need for only one operation to be performed one thread at a time.
In the special circumstances of the pi-computing code, we can use an atomic operation rather than a critical section. Atomic operations are limited to “storage” access, which in this context means memory. The operation can read the storage location, write to it, or update it. These specifics can be added as clauses to the construct, but are not usually necessary and were added in recent versions of the standard.
Without a clause (or when the clause is update), the following operations are supported:
x++; // C/C++
x--; // C/C++
++x; // C/C++
--x; // C/C++
x binop= expr
x = x binop expr
x = expr binop x
where binop is one of one of +, *, -, /, &, ^, |, <<, or >>. The operation must be supported by the underlying language. The expr is an arithmetic expression with the permitted operations.
The atomic directive applies only to the statement following it.
Atomic operations can have lower overhead than using a critical section for the same statement, and if the architecture supports it they can use hardware atomic operations to further reduce overhead. Also, if two independent statements are critical, they can each be atomic and different threads can execute them at the same time, as long as only one thread performs the operation.
Atomic operations still serialize the code, however.
Syntax
C/C++
#pragma omp atomic <clause>
Fortran
!$omp atomic <clause>
! optional
!$omp end atomic
The end is optional since atomic is only valid for the following statement.
Python Omp4py does not seem to have fully implemented this yet.
Exercise
Modify the pi code to use an atomic construct.
Solutions
C
Contents of omp_atomic.c
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>
int main() {
double area, pi, x;
int n;
n=1000;
area=0.0;
for (int i=0; i< n; i++) {
x=(i+0.5)/n;
#pragma omp atomic
area+=4.0/(1.0+x*x);
}
pi=area/n;
printf("Pi is %f\n",pi);
return(0);
}
Download omp_atomic.c file
Fortran
Contents of omp_atomic.f90
program pie
use omp_lib
implicit none
double precision :: area, pi, x
integer :: i, n
integer :: nthreads
n=10000
area=0.0
!$omp parallel do private(x)
do i=1,n
x=(i+0.5d0)/n
!$omp atomic
area=area+4.0/(1.0d0+x**2)
enddo
!$omp end parallel do
pi=area/n
write(*,'(a,f9.6)') "Pi is ", pi
end program
Download omp_atomic.f90 file