Сокращение времени запуска ядер в пошаговом цикле - OpenACC - PullRequest
0 голосов
/ 25 января 2019

Я пытаюсь внедрить 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

Ответы [ 2 ]

0 голосов
/ 26 января 2019

Когда вы говорите «большой промежуток времени», можете ли вы быть более конкретным? Вы берете секунды, микросекунды, миллисекунды? Хотя он сильно варьируется, я ожидаю, что издержки при запуске ядра будут составлять около 40 микросекунд. Часто издержки запуска теряются в шуме, но если ядра работают особенно быстро или если ядро ​​запускается миллионы раз, это может повлиять на относительную производительность. Использование предложений «async» может помочь скрыть издержки запуска (см. Ниже).

Хотя, если промежутки намного больше, тогда может быть что-то еще. Например, если есть сокращение в цикле, переменная сокращения может быть скопирована обратно на хост. Обратите внимание, если вы используете PGI, взгляните на сообщения обратной связи компилятора (-Minfo = accel). Это может дать некоторые подсказки о том, что происходит.

  1. Есть ли способ запустить эти ядра во время работы других ядер?

Да. Используйте три отдельных области "ядра", по одному на каждую часть. Затем добавьте предложение «async (1)» в каждую вычислительную область. После запуска ядер Async продолжит работу хоста и, поскольку они используют одну и ту же очередь, 1 в этом случае, но вы можете использовать любое положительное целое число, он создаст зависимость, поэтому B не будет работать до завершения A, а C будет запускаться после B Вы захотите добавить «! $ Acc wait», где вы хотите, чтобы хост синхронизировался с устройством.

Заметка под капотом, асинхронные очереди отображаются в поток CUDA.

cuStreamSynchronize также блокирует работу графического процессора, приводя к дополнительному нулевому времени. Это похоже на вопрос как заставить другие ядра запускаться до конца времени шаговая петля.

Это время, когда хост блокируется в ожидании завершения вычислений на GPU. Оно должно быть примерно таким же, как и во время работы вашего ядра (если не используется async).

0 голосов
/ 25 января 2019

Вы можете оценить разницу между WDDM, если ваш графический процессор работает без дисплея.Если к нему не подключено ни одного дисплея, это устраняет некоторые проблемы работы с WDDM.

Попробуйте подключить ваши дисплеи к материнской плате, если она поддерживает драйверы для интегрированной графики и графического процессора одновременно (проверьте BIOS).

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

...