С технической точки зрения, код вашего примера неверен, так как вы используете COPYIN
для инициализации копий threadprivate данными из неинициализированного COMMON BLOCK
. Но это не причина гонки данных — добавление оператора DATA
или простое присвоение x
, y
и z
до того, как параллельная область не изменит результат.
Это либо (очень старая) ошибка компилятора Intel Fortran, либо Intel странно интерпретирует текст стандарта OpenMP (раздел 2.15.4.1 текущей версии):
Копия выполняется, как бы по заданию, после формирования команды и до начала выполнения связанного структурированного блока.
Intel реализует выделенный текст, вставляя memcpy
в начале описанной процедуры. Другими словами:
!$OMP PARALLEL DO COPYIN(/firstcomm/)
do i = 1, 3000
...
end do
!$OMP END PARALLEL DO
становится (в смеси Фортрана и псевдокода):
par_region0:
my_firstcomm = get_threadprivate_copy(/firstcomm/)
if (my_firstcomm != firstcomm) then
memcpy(my_firstcomm, firstcomm, size of firstcomm)
end if
// Actual implementation of the DO worksharing construct
call determine_iterations(1, 3000, low_it, high_it)
do i = low_it, high_it
...
... my_firstcomm used here instead of firstcomm
...
end do
call openmp_barrier
end par_region0
MAIN:
// Prepare a parallel region with 3 threads
// and fire the outlined code in the worker threads
call start_parallel_region(3, par_region0)
// Fire the outlined code in the master thread
call par_region0
call end_parallel_region
Описанная процедура сначала находит адрес частной копии общего блока, а затем сравнивает этот адрес с адресом самого общего блока. Если оба адреса совпадают, то код выполняется в главном потоке и копия не требуется, в противном случае вызывается memcpy
для создания побитовой копии данных мастера в блоке threadprivate.
Теперь можно было бы ожидать, что в конце части инициализации и прямо перед началом цикла должен быть барьер, и хотя Сотрудники Intel утверждают, что он есть, но его нет (проверено с ifort 11.0, 14.0 и 16.0). Более того, компилятор Intel Fortran не учитывает список переменных в предложении COPYIN
и копирует весь общий блок, если какая-либо содержащаяся в нем переменная указана в предложении, т. е. COPYIN(x)
обрабатывается так же, как COPYIN(/firstcomm/)
.
Являются ли это ошибками или особенностями компилятора Intel Fortran Compiler, только Intel может сказать. Также может быть, что я неправильно читаю вывод сборки. Если кто-нибудь сможет найти отсутствующий барьер, сообщите мне об этом. Одним из возможных обходных путей может быть разделение комбинированной директивы и вставка явного барьера перед конструкцией совместной работы:
!$OMP PARALLEL COPYIN(/firstcomm/) PRIVATE(I)
!$OMP BARRIER
!$OMP DO
do i = 1, 3000
z = 3.D0
y = z+log10(z)
x = y+z
end do
!$OMP END DO
!$OMP END PARALLEL
С этим изменением гонка данных переместится в инициализацию внутренней таблицы диспетчеризации в вызове log10
, что, вероятно, является ложным срабатыванием.
GCC реализует COPYIN
по-другому. Он создает общую копию данных threadprivate главного потока, которая копирует их, а затем передает рабочим потокам для использования в процессе копирования.
person
Hristo Iliev
schedule
26.01.2016
THREADPRIVATE
непосредственно в переменныеx,y,z
, а не в общее имя блокаfirstcomm
. Я думаю, что реализации openmp могут не проверять, находится ли переменная в блокеcommon
, а затем генерировать ложное срабатывание предупреждения там, где его быть не должно. Это единственная проблема, которую я вижу в вашем коде. - person innoSPG   schedule 26.01.2016!$OMP THREADPRIVATE
в общем блоке(ах), так как моя реальная программа использует переменные в общем блоке в подпрограммах, вызываемых из параллельной области, не передавая их в качестве аргументов (код примера не отражает это, мой плохой), и размерPRIVATE
является лексическим, только если он не передается функциям/подпрограммам в качестве аргумента. Интересно, что использование подходаPRIVATE
приводит к быстрому сбою моей программы, что говорит о том, что исходный код на самом деле может быть правильным. - person marcus_a   schedule 26.01.2016