I have a parallel region in my fortran code that uses OpenMP and calls subroutines that use variables with the save attribute in their scope. This is causing a problem because they are shared between threads, so my question is if there is a way to make these variables private while still being saved between subroutine calls, or will I need to input and output them?
Thanks
You can do it using threadprivate
- the code below shows a couple of slightly different approaches. But please note
a) The values are only guaranteed to be preserved between parallel regions if the parallel regions use the same number of threads
b) Please think carefully whether you really need save
, save and parallel programming are very rarely good bed fellows. There are one or two good uses (see e.g. Fortran OpenMP with subroutines and functions), but if there is an alternative way to do what you want to do (e.g. passing through the argument list) this will almost certainly cause you less pain in the long run
(For some reason using a proper list breaks the formatting of the code below - if someone knows how to fix that, thanks!)
ian@eris:~/work/stack$ cat threadprivate.f90
Program test
Implicit None
Call do_something
Call do_something
Call do_something
Write( *, * )
!$omp parallel
Call do_something_else
Call do_something_else
Call do_something_else
!$omp end parallel
Contains
Subroutine do_something
Use omp_lib
Implicit None
Integer, Save :: calls = 0
Integer, Save :: stuff
Logical, Save :: first = .True.
!$omp threadprivate( first, stuff )
calls = calls + 1
! Shouldn't scope threadprivate variables - they are already private
!$omp parallel default( none ) shared( calls )
If( first ) Then
first = .False.
stuff = omp_get_thread_num()
Else
stuff = stuff + 1
End If
Write( *, '( 3( a, 1x, i2, 1x ) )' ) 'do something call ', calls, &
'thread = ', omp_get_thread_num(), 'stuff = ', stuff
!$omp end parallel
End Subroutine do_something
Subroutine do_something_else
Use omp_lib
Implicit None
Integer, Save :: calls = 0
Integer, Save :: stuff
Logical, Save :: first = .True.
!$omp threadprivate( first, stuff, calls )
calls = calls + 1
If( first ) Then
first = .False.
stuff = omp_get_thread_num()
Else
stuff = stuff + 1
End If
Write( *, '( 3( a, 1x, i2, 1x ) )' ) 'do something else call ', calls, &
'thread = ', omp_get_thread_num(), 'stuff = ', stuff
End Subroutine do_something_else
End Program test
ian@eris:~/work/stack$ gfortran -std=f2008 -Wall -Wextra -O -g -fcheck=all -pedantic -fopenmp threadprivate.f90
ian@eris:~/work/stack$ export OMP_NUM_THREADS=2
ian@eris:~/work/stack$ ./a.out
do something call 1 thread = 0 stuff = 0
do something call 1 thread = 1 stuff = 1
do something call 2 thread = 1 stuff = 2
do something call 2 thread = 0 stuff = 1
do something call 3 thread = 1 stuff = 3
do something call 3 thread = 0 stuff = 2
do something else call 1 thread = 1 stuff = 1
do something else call 2 thread = 1 stuff = 2
do something else call 3 thread = 1 stuff = 3
do something else call 1 thread = 0 stuff = 0
do something else call 2 thread = 0 stuff = 1
do something else call 3 thread = 0 stuff = 2
ian@eris:~/work/stack$ export OMP_NUM_THREADS=4
ian@eris:~/work/stack$ ./a.out
do something call 1 thread = 3 stuff = 3
do something call 1 thread = 2 stuff = 2
do something call 1 thread = 1 stuff = 1
do something call 1 thread = 0 stuff = 0
do something call 2 thread = 1 stuff = 2
do something call 2 thread = 3 stuff = 4
do something call 2 thread = 0 stuff = 1
do something call 2 thread = 2 stuff = 3
do something call 3 thread = 3 stuff = 5
do something call 3 thread = 1 stuff = 3
do something call 3 thread = 0 stuff = 2
do something call 3 thread = 2 stuff = 4
do something else call 1 thread = 3 stuff = 3
do something else call 2 thread = 3 stuff = 4
do something else call 3 thread = 3 stuff = 5
do something else call 1 thread = 1 stuff = 1
do something else call 2 thread = 1 stuff = 2
do something else call 3 thread = 1 stuff = 3
do something else call 1 thread = 0 stuff = 0
do something else call 2 thread = 0 stuff = 1
do something else call 3 thread = 0 stuff = 2
do something else call 1 thread = 2 stuff = 2
do something else call 2 thread = 2 stuff = 3
do something else call 3 thread = 2 stuff = 4
ian@eris:~/work/stack$