ОК, речь идет о различии между лексической и динамической степенью директив OpenMP и взаимодействием с переменным охватом. Лексическая степень директивы - это текст между началом и концом структурированного блока, следующий за директивой. Динамическая степень - это лексическая степень плюс операторы, выполняемые как часть любой подпрограммы, выполняемой в результате выражений в лексической степени. Так что в чем-то вроде
Program stuff
Implicit None
Real, Dimension(1:100) :: a
Call Random_number(a)
!$omp parallel default(none) shared(a)
Call sub(a)
!$omp end parallel
Contains
Subroutine sub(a)
Real, Dimension(:), Intent(InOut) :: a
Integer :: i
!$omp do
Do i = 1, Size(a)
a(i) = 2.0 * a(i)
End Do
End Subroutine Sub
End Program stuff
(полностью тестировался, написанный прямо в здесь) лексической степени параллельной области, инициированной! $ OMP параллельно просто
Call sub(a)
в то время как динамическая степень является вызовом и содержание подпрограммы. И для полноты терминологии! $ Omp do является примером осиротевшей директивы, директивы, которая не находится в лексической степени другой директивы, но в динамической степени. См
https://computing.llnl.gov/tutorials/openMP/#Scoping
для другого примера.
Почему это имеет значение? Ну, вы можете явно указывать переменные для сущностей в лексической области, а только массив a в этом случае, но для объектов, которые становятся определяемыми из-за выполняемой динамической степени, вы не можете этого сделать! Вместо OpenMP имеет ряд правил, которые в простых условиях являются
- Области аргументов подпрограммы унаследованный от точки вызова, то есть, если вы контекстные их как частные в начале лексической степени они остаются частными, и если они разделены, они остаются общедоступными.
- Локальные переменные для подпрограмм по умолчанию являются частными (поэтому я в приведенном выше примере является закрытым, как вы хотите), если они не объявлены с помощью атрибута SAVE (явно или неявно), и в этом случае они являются общими.
И в моем опыте, который доставит вам большую часть пути! Использование динамической степени в сочетании с сиротскими директивами - отличный способ держать под контролем программу OpenMP, и я должен сказать, что я не согласен с вышеуказанным комментарием, я считаю, что сиротские директивы по работе очень полезны!Таким образом, вы можете объединить все вышеперечисленное делать такие вещи, как
Program dot_test
Implicit None
Real, Dimension(1:100) :: a, b
Real :: r
a = 1.0
b = 2.0
!$omp parallel default(none) shared(a, b, r)
Call dot(a, b, r)
Write(*, *) r
!$omp end parallel
Contains
Subroutine dot(a, b, r)
Real, Dimension(:), Intent(In ) :: a, b
Real, Intent( Out) :: r
Real, Save :: s
Integer :: i
!$omp single
s = 0.0
!$omp end single
!$omp do reduction(+:s)
Do i = 1, Size(a)
s = s + a(i) * b(i)
End Do
!$omp end do
!$omp single
r = s
!$omp end single
End Subroutine dot
End Program dot_test
Wot now? gfortran -std=f95 -fopenmp -O -Wall -Wextra dot.f90
Wot now? export OMP_NUM_THREADS=3
Wot now? ./a.out
200.000000
200.000000
200.000000
Эта простая ситуация несколько усложняется модулей переменных и общих блоков, так что не использовать глобальные переменные ... но если вы они должны по своей default shared, если не объявлено как threadprivate. См
https://computing.llnl.gov/tutorials/openMP/#THREADPRIVATE
для примера.
Показать пример такой функции. Если локальные переменные не 'save', они не являются проблемой. –
См. Http://stackoverflow.com/questions/22381795/is-there-an-easy-way-to-prepare-fortran-code-for-parallel-invocation. Эти ключевые слова, которые вы хотите исследовать, - это * thread-safety * и * поточно-безопасные * процедуры. –
@ VladimirF, чтобы уточнить, у меня есть код для двух других, так что это скорее вопрос синтаксиса, чем безопасность потоков. «SAVE» находится в миксе, и подпрограмма и функция получают LOT, поэтому важно избегать ненужных повторных выделения памяти. Хотя, если использование 'SAVE' подвешивает их к куче, (и ущемляет производительность), тогда это не стоит. Тестирование может быть в порядке. – TTT