Использование атрибута fortran save в параллельных регионах - PullRequest
2 голосов
/ 04 апреля 2020

У меня есть параллельная область в моем коде Fortran, который использует OpenMP и вызывает подпрограммы, которые используют переменные с атрибутом save в своей области видимости. Это вызывает проблему, потому что они разделяются между потоками, поэтому мой вопрос заключается в том, есть ли способ сделать эти переменные частными, сохраняя их между вызовами подпрограмм, или мне нужно будет вводить и выводить их?

Спасибо

1 Ответ

4 голосов
/ 05 апреля 2020

Вы можете сделать это, используя threadprivate - код ниже демонстрирует несколько несколько разных подходов. Но обратите внимание

a) Значения гарантированно сохраняются между параллельными областями только в том случае, если параллельные области используют одинаковое количество потоков

b) Пожалуйста, тщательно продумайте, действительно ли вам нужна save , сохранить и параллельное программирование очень редко хороших собратьев. Есть одно или два хороших применения (см., Например, Fortran OpenMP с подпрограммами и функциями ), но если есть альтернативный способ сделать то, что вы хотите сделать (например, пройти через список аргументов), это почти наверняка в долгосрочной перспективе вам будет меньше боли

(По какой-то причине использование правильного списка нарушает форматирование кода ниже - если кто-то знает, как это исправить, спасибо!)

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$ 
...