Я пытаюсь внедрить OpenACC в какой-то код Fortran Код состоит из внешнего пошагового цикла (который нельзя распараллелить), и внутри цикла есть несколько вложенных циклов. Эти вложенные циклы могут быть распараллелены, но их необходимо выполнять по порядку (то есть A, а затем B и C).
Я хочу переложить весь этот процесс на графический процессор, поскольку передача данных через многократные временные шаги между графическим процессором и процессором становится непомерным штрафом. Псевдокод ниже иллюстрирует мой текущий подход:
!$acc data copy(ALL THE DATA THAT I NEED)
DO iter = 1, NT
value = array_of_values(iter)
!$ acc kernels
!PART A
!$acc loop independent, private(j)
DO J=0,ymax
!$acc loop independent, private(i)
DO I=0,xmaxput
!$acc loop independent, private(l)
DO L=0,zmax
if(value == 0) then
(DO SOME COMPUTATIONS...)
elseif(value < 0) then
(DO SOME OTHER COMPUTATIONS...)
elseif(value > 0) then
(DO SOME OTHER COMPUTATIONS...)
endif
ENDDO
ENDDO
ENDDO
!NOW GO DO OTHER STUFF
!PART B
!$acc loop independent, private(j)
DO J=0,ymax
!$acc loop independent, private(i)
DO I=0,xmax
!$acc loop independent, private(l)
DO L=0,zmax
(DO SOME EVEN MORE COMPUTATIONS...)
ENDDO
ENDDO
ENDDO
!PART C
!etc...
!$acc end kernels
ENDDO
!$acc end data
У меня есть рабочий код, использующий этот подход, однако, когда я профилирую его на графическом процессоре GeForce MX150, используя визуальный профилировщик NVIDIA ( щелкните для изображения ), я вижу, что каждая итерация в цикле шагания по времени приводит к большому промежутки времени, когда вычисления не проводятся. Driver Driver говорит, что в течение этого времени он выполняет "cuLaunchKernel". Если я просто скопирую весь цикл так, чтобы вместо одного шага выполнялись две итерации, то этот разрыв не будет существовать в цикле с пошаговым изменением времени, только когда цикл начинается.
У меня есть несколько (взаимосвязанных) вопросов:
1. Есть ли способ запустить эти ядра во время работы других ядер?
2. Я прочитал здесь и здесь , что драйвер WDDM запускает запуск ядра, который, по-видимому, происходит здесь. Означает ли это, что если я буду работать в Linux, я не должен ожидать такого поведения?
cuStreamSynchronize также блокирует работу графического процессора, что приводит к дополнительному нулевому времени. Похоже, это связано с вопросом о том, как заставить другие ядра запускаться до конца цикла пошагового выполнения.
Я впервые использую OpenACC. Я искал ответ на этот вопрос, но, вероятно, использую неправильные ключевые слова, так как не смог ничего найти.
РЕДАКТИРОВАТЬ - решение
В соответствии с предложением Мата я добавил Async, который решил проблему. Интересно, что запуск ядра по-прежнему выполняется в одно и то же время, но теперь каждое ядро, которое будет запускаться во время итерации цикла пошагового перехода по времени, запускается сразу в начале программы. Ниже приведен обновленный псевдокод, а также несколько других настроек, которые могут пригодиться кому-либо еще:
!$acc data copy(ALL THE DATA THAT I NEED)
!$acc wait
DO iter = 1, NT
value = array_of_values(iter)
!$acc update device(value, iter), async(1) !allows loop to run on cpu and sends value to GPU
!PART A
!$acc kernels, async(1)
!$acc loop independent, private(j)
DO J=0,ymax
!$acc loop independent, private(i)
DO I=0,xmaxput
!$acc loop independent, private(l)
DO L=0,zmax
if(value == 0) then
(DO SOME COMPUTATIONS...)
elseif(value < 0) then
(DO SOME OTHER COMPUTATIONS...)
elseif(value > 0) then
(DO SOME OTHER COMPUTATIONS...)
endif
ENDDO
ENDDO
ENDDO
!$acc end kernels
!NOW GO DO OTHER STUFF
!PART B
!$acc kernels, async(1)
!$acc loop independent, private(j)
DO J=0,ymax
!$acc loop independent, private(i)
DO I=0,xmax
!$acc loop independent, private(l)
DO L=0,zmax
(DO SOME EVEN MORE COMPUTATIONS...)
ENDDO
ENDDO
ENDDO
!$acc end kernels
!PART C
!$acc kernels, async(1)
!for loops, etc...
!$acc end kernels
ENDDO
!$acc wait
!$acc end data