Объединение CUDA с Python ODEInt и Parallel Reduction

Я аспирант по биофизике, пытаюсь запрограммировать модель агрегации белков с использованием PyCUDA и Scipy's ODEInt. За последние две недели я запустил код, но он очень медленный. Посмотрим, смогу ли я объяснить, что делает мой код.

У меня есть np массив N концентраций, где каждый элемент представляет собой концентрацию полимера длиной i+1. У меня есть функция, которая вычисляет скорость изменения концентраций полимера с использованием CUDA, где каждое ядро ​​вычисляет скорость изменения одного полимера определенной длины. Во время этого вычисления поток должен суммировать массив длины (N-i-1), что резко замедляет мой код.

Немного почитав и погуглив, я наткнулся на параллельное сокращение как способ вызвать параллелизм, чтобы последовательные вычисления, такие как сумма массива, выполнялись намного быстрее. Конечно, я имею в виду слайды PowerPoint от Марка Харриса. Это было отличное чтение, и это похоже на потенциальный способ резко ускорить мой код, но у меня есть несколько вопросов:

Если количество полимеров N должно составлять ~ 8700-9000, возможно ли использовать CUDA для одновременного сокращения этих N массивов? Выполняя быстрые вычисления (опять же, благодаря отличному объяснению SO о том, как рассчитать максимальное количество одновременных потоков), я получаю для своего GTX Titan, что у меня может быть 15 * 64 * 32 = 30720 потоков одновременно. Если я вызываю свое ядро ​​на ~ 8960 ядрах за раз, у меня останется только 21760 потоков, верно? Поскольку кажется, что вам нужно как минимум (длина массива / 2) потоков, чтобы правильно его уменьшить, то я обречен.

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

Не знаю ... Я просто аспирант по физике. Я думал, что спрошу у профессионалов, прежде чем отправиться в долгое путешествие в неверном направлении. Можно ли легко и эффективно сказать ядру, что нужно что-то уменьшить?

Спасибо, Карстен

Вот представление того, что я пытаюсь сделать.

fluxes and concs are np.arrays
dcdt(concs, t)
    Call CUDA to calculate fluxes
        Thread
        0       fluxes[i] = stuff + sum(concs[n] for n from 1 to 9000)
        1       fluxes[i] = stuff + sum(concs[n] for n from 2 to 9000)
        2       fluxes[i] = stuff + sum(concs[n] for n from 3 to 9000)
        ...
        N       fluxes[i] = stuff

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


person Hair of Slytherin    schedule 14.05.2013    source источник
comment
Да! Я отправляю массив концентраций в графический процессор, а затем вычисляю скорость изменения каждой концентрации с помощью CUDA.   -  person Hair of Slytherin    schedule 15.05.2013


Ответы (1)


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

Если размер данных одного массива не превышает ~ 2 ГБ, количество потоков вряд ли будет проблемой.

Вы можете создать ядро, которое просто последовательно уменьшает массивы один за другим. Кажется, вы говорите, что существует N массивов, где N около 9000. Насколько велик каждый массив? Если массивы достаточно велики, примерно вся мощность графического процессора (грубо говоря) может быть задействована для каждой отдельной операции, в этом случае нет значительного штрафа при уменьшении массивов один за другим. Тогда ядро ​​могло бы быть базовой параллельной редукцией, которая перебирала бы массивы. Должно быть довольно просто.

Если у вас есть примерно 9000 массивов для обработки, и нетрудно упорядочить ваши данные с чередованием, тогда вы также можете рассмотреть массив из 9000 потоков, где каждый поток суммирует элементы одного массива в последовательном цикле, почти так же, как вы бы наивно это делали с кодом ЦП. Организация данных здесь будет иметь решающее значение, потому что цель всего этого - максимально использовать доступную полосу пропускания памяти. Поскольку цикл в каждом потоке подбирает следующий элемент данных для суммирования, вы должны убедиться, что у вас есть непрерывные чтения данных между потоками в деформации (объединенный доступ), что подразумевает организацию хранения чередующихся данных среди ваших N массивов. Если бы это было так, этот подход также работал бы довольно быстро.

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

person Robert Crovella    schedule 14.05.2013
comment
Вы также можете использовать odeint, который совместим с Thrust и позволит вам решать ODE в целом на C ++. - person headmyshoulder; 15.05.2013
comment
Спасибо за развернутый ответ! Я не уверен, что мне следует отвечать на ваш ответ или на свой, но начнем. Прежде всего, ODEInt требует вызываемой функции для расчета скорости изменений. В этой вызываемой функции я отправляю массив концентраций в графический процессор, чтобы CUDA мог выполнять вычисления. Что касается размеров массивов, то их около 9000 float64. Я не уверен в чередовании и что я это очень хорошо понимаю, но я смотрю на тягу и объединяю ее с pyCUDA. Я собираюсь отредактировать свой вопрос и, надеюсь, это немного больше объяснит то, что я делаю. - person Hair of Slytherin; 15.05.2013