NERSC logo National Energy Research Scientific Computing Center
  A DOE Office of Science User Facility
  at Lawrence Berkeley National Laboratory
 

CRITICAL Directive

The CRITICAL and END CRITICAL directives restrict access to the enclosed code to only one thread at a time. A thread waits at the beginning of the critical section until no other thread in the team is executing a critical section with the same name.

 !$OMP CRITICAL   [(name)]   
	block                   
 !$OMP END CRITICAL [(name)]  

Bad Example

The following example will cause problems without the use of a critical region.

!Filename: critical.f90
!
!This shows use of the CRITICAL directive.
!

PROGRAM CRITICAL 
        IMPLICIT NONE
        INTEGER:: L,I
        INTEGER:: nthreads, OMP_GET_NUM_THREADS

        L=10

!$OMP PARALLEL SHARED(L) PRIVATE(nthreads,I)

!$OMP MASTER
        nthreads = OMP_GET_NUM_THREADS()
        PRINT *, "Number of threads:",nthreads
!$OMP END MASTER

!*** The following is going to cause problems without a
!*** CRITICAL directive

        CALL ADD_ONE(L)

!$OMP END PARALLEL

        PRINT *, "The final value of L is", L


END PROGRAM CRITICAL 

SUBROUTINE ADD_ONE(I)
        IMPLICIT NONE
        INTEGER, INTENT(INOUT):: I
	INTEGER:: J, K

	J = I

!$OMP MASTER
        OPEN(UNIT=26,FORM='FORMATTED',FILE='junk')
        DO K=1,40000
                WRITE(26,*) "Hi Mom!"
        END DO
        CLOSE(26)
!$OMP END MASTER


        J = J + 1

	I = J

END SUBROUTINE ADD_ONE

We can see the problem by compiling and running on franklin.

> cat critical.pbs
#PBS -N critical
#PBS -j oe
#PBS -o critical.out
#PBS -q interactive
#PBS -S /bin/bash
#PBS -l mppwidth=1
#PBS -l mppnppn=1
#PBS -l mppdepth=2
#PBS -l walltime=00:05:00
#PBS -V

cd $PBS_O_WORKDIR

ftn -o critical -mp=nonuma -Minfo=mp critical.f90

export OMP_NUM_THREADS=2
for i in 1 2 3 4 5
do
  echo "run $i of critical"
  aprun -n 1 -N 1 ./critical
done
> qsub critical.pbs
500824.nid00003
> cat critical.out
/opt/xt-pe/2.0.44a2/bin/snos64/ftn: INFO: linux target is being used
critical.f90:
critical:
    13, Parallel region activated
    15, Begin master section
    18, End master section
    23, Parallel region terminated
add_one:
    39, Begin master section
    45, End master section
run 1 of critical
 Number of threads:            2
 The final value of L is           11
Application 4735728 resources: utime 2, stime 0
run 2 of critical
 Number of threads:            2
 The final value of L is           12
Application 4735729 resources: utime 1, stime 0
run 3 of critical
 Number of threads:            2
 The final value of L is           11
Application 4735730 resources: utime 1, stime 0
run 4 of critical
 Number of threads:            2
 The final value of L is           11
Application 4735731 resources: utime 1, stime 0
run 5 of critical
 Number of threads:            2
 The final value of L is           11
Application 4735732 resources: utime 1, stime 0

Not only do we not get the expected answer of 12 each time (10 + the number of threads), but we get different answers for different runs. What's the problem?

The error occurs when we call the subroutine ADD_ONE, which adds one to the shared variable L. Each thread passes the address of L at the time it gets to the ADD_ONE call. But the order in which that happens is undefined. One thread makes the call while another is the process of doing the addition but has not yet updated the value. The result is indeterminate. (The "Hi Mom!" write statement is there just to ensure that the threads get well out of synch.)

We can fix the problem (although perhaps not efficiently) by allowing only one thread at a time to update the value of L. We do this by using a CRITICAL region.

Example (fixed)

.
.
.
!$OMP CRITICAL
	CALL ADD_ONE(L)
!$OMP END CRITICAL
.
.
.

Now we get the right answer consistently

> cat critical_fixed.pbs
#PBS -N critical_fixed
#PBS -j oe
#PBS -o critical_fixed.out
#PBS -q interactive
#PBS -S /bin/bash
#PBS -l mppwidth=1
#PBS -l mppnppn=1
#PBS -l mppdepth=2
#PBS -l walltime=00:05:00
#PBS -V

cd $PBS_O_WORKDIR

ftn -o critical_fixed -mp=nonuma -Minfo=mp critical_fixed.f90

export OMP_NUM_THREADS=2
for i in 1 2 3 4 5
do
  echo "run $i of critical_fixed"
  aprun -n 1 -N 1 ./critical_fixed
done
> qsub critical_fixed.pbs
500850.nid00003
> cat critical_fixed.out
/opt/xt-pe/2.0.44a2/bin/snos64/ftn: INFO: linux target is being used
critical_fixed.f90:
critical:
    13, Parallel region activated
    15, Begin master section
    18, End master section
    24, Begin critical section __cs_unspc
        End critical section __cs_unspc
        Parallel region terminated
add_one:
    41, Begin master section
    47, End master section
run 1 of critical_fixed
 Number of threads:            2
 The final value of L is           12
Application 4735856 resources: utime 1, stime 0
run 2 of critical_fixed
 Number of threads:            2
 The final value of L is           12
Application 4735857 resources: utime 0, stime 0
run 3 of critical_fixed
 Number of threads:            2
 The final value of L is           12
Application 4735858 resources: utime 1, stime 0
run 4 of critical_fixed
 Number of threads:            2
 The final value of L is           12
Application 4735859 resources: utime 1, stime 0
run 5 of critical_fixed
 Number of threads:            2
 The final value of L is           12
Application 4735860 resources: utime 1, stime 0

as expected.


LBNL Home
Page last modified: Mon, 05 May 2008 21:14:43 GMT
Page URL: http://www.nersc.gov/nusers/help/tutorials/openmp/critical.php
Web contact: webmaster@nersc.gov
Computing questions: consult@nersc.gov

Privacy and Security Notice
DOE Office of Science