2013-09-07 2 views
3

Я пытаюсь распараллелить петлю fortran через OpenMP. Цикл, по существу состоит только из двух команд:fortran openmp synchronization

do i=1,LSample 
    calcSslice(Vpot(:,:,i), Sslice) 
    rpold = rp 
    combine_rp_matrices (rpold, Sslice, rp) 
end do 

calcSslice подпрограмма считывает Vpot (:,:, я), выполняет некоторые вычисления и сохраняет результаты в матрице Sslice. comb_rp_matrices использует rpold и Sslice для обновления rp. rp действует как текущая переменная и является желаемым выходом программы. Порядок, в котором матрицы Sslice от разных итераций объединены с rp, не имеет значения. Моя первая попытка распараллеливания этого цикла выглядела так:

!$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(Sslice), SCHEDULE(DYNAMIC) 
do i=1,LSample 
    calcSslice(Vpot(:,:,i), Sslice) 
!$OMP CRITICAL 
    rpold = rp 
    combine_rp_matrices (rpold, Sslice, rp) 
!$OMP END CRITICAL 
end do 
!$OMP END PARALLEL DO 

Это компилируется и запускается, но приводит к неправильным результатам. Используя следующий код я получить правильные результаты, но гораздо медленнее исполнения (хотя еще быстрее, чем сериализованном код):

!$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(Sslice), SCHEDULE(DYNAMIC) 
do i=1,LSample 
!$OMP CRITICAL(Crit2) 
    calcSslice(Vpot(:,:,i), Sslice) 
!$OMP END CRITICAL(Crit2) 
!$OMP CRITICAL 
    rpold = rp 
    combine_rp_matrices (rpold, Sslice, rp) 
!$OMP END CRITICAL 
end do 
!$OMP END PARALLEL DO 

Так что, по-видимому какой-то вопрос синхронизации с calcSslice. Однако я не совсем понимаю, где это произойдет. Vpot считывается и не записывается в calcSslice, а Sslice - переменная threadprivate. Любые глобальные переменные, используемые в calcSslice, также считываются только из. Переменные rpold и rp объявляются в области подпрограммы, в которой цикл DO является частью, и поэтому не может быть доступен calcSslice. Переменные, объявленные в calcSslice, используют следующие атрибуты: aim (in), target (out), target, pointer.

Где это происходит не так?

EDIT: проблема решена, причиной была инициализация переменных в calcSslice во время объявления, что подразумевает атрибут save.

ответ

2

Я предполагаю, что calcSslice не является потоковым. Убедитесь, что эта подпрограмма не имеет доступа к глобальным переменным, отличным от «только для чтения», и не использует атрибут save (остерегайтесь неявного сохранения, если вы инициализируете переменные во время объявления!). Вы можете использовать threadchecker, как тот, который предоставил Intel, чтобы найти условия гонки в вашем коде. Если у вас нет доступа к такому программному обеспечению, я бы начал с фиктивной процедуры, а затем заполнил процедуру incrementaly, чтобы увидеть, где она терпит неудачу.

Еще одна вещь, которая меня озадачивает, - это последние две строки тела петли. Каждый поток создает резервную копию всей матрицы, а затем добавляет свой фрагмент. Не лучше ли было бы собирать все фрагменты (например, с помощью предложения reduction), а затем объединить этот большой фрагмент один раз?

+0

Благодарим вас за ответ. Есть некоторые указатели, которые инициализируются как null() во время объявления. Я не знал, что это означает атрибут save. Я изменю это и посмотрю, поможет ли это. –

+0

'Combine_rp_matrices' использует' Sslice' для обновления 'rp'. Мне пришлось бы собирать 'Sslice'-переменные, которые в 4 раза больше, чем' rp' матрицы. Матрицы очень большие и не подходят для стека, поэтому я фактически использовал массив переменных '' NThreads'Sslice' вместо того, чтобы объявить его закрытым. Для небольших матриц проблема, которую я описал, сохраняется даже при использовании частного 'Sslice', поэтому я не упоминал об этом в своем вопросе.' NThreads' намного меньше, чем 'LSample', массив' LSample' 'Sslice 's будет использовать слишком много памяти. –

Смежные вопросы