Параллельное программирование на вложенном цикле for в Python с использованием PyCuda (или еще?)

Часть моей функции Python выглядит так:

for i in range(0, len(longitude_aq)):
    center = Coordinates(latitude_aq[i], longitude_aq[i])
    currentAq = aq[i, :]
    for j in range(0, len(longitude_meo)):
        currentMeo = meo[j, :]
        grid_point = Coordinates(latitude_meo[j], longitude_meo[j])
        if is_in_circle(center, RADIUS, grid_point):
            if currentAq[TIME_AQ] == currentMeo[TIME_MEO]:
                humidity += currentMeo[HUMIDITY_MEO]
                pressure += currentMeo[PRESSURE_MEO]
                temperature += currentMeo[TEMPERATURE_MEO]
                wind_speed += currentMeo[WIND_SPEED_MEO]
                wind_direction += currentMeo[WIND_DIRECTION_MEO]
                count += 1.0

    if count != 0.0:
        final_tmp[i, HUMIDITY_FINAL] = humidity/count
        final_tmp[i, PRESSURE_FINAL] = pressure/count
        final_tmp[i, TEMPERATURE_FINAL] = temperature/count
        final_tmp[i, WIND_SPEED_FINAL] = wind_speed/count
        final_tmp[i, WIND_DIRECTION_FINAL] = wind_direction/count

    humidity, pressure, temperature, wind_speed, wind_direction, count = 0.0, 0.0, 0.0, 0.0, 0.0, 0.0

final.loc[:, :] = final_tmp[:, :]

Проблема: len(longitude_aq) составляет прибл. 320k и len(longitude_meo) это 7 миллионов. Что приближает этот код к 2100 миллиардам итераций...

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

Не похоже, что я могу действовать по-другому.

Возможное решение: параллельное программирование. Мой университет разрешает мне доступ к их HPC. У них есть несколько доступных узлов + несколько графических процессоров (перечислите здесь для GPU и здесь для CPU)

Цель: не имея опыта программирования CUDA с Python, мне интересно, как проще всего преобразовать мой код во что-то, что может выполняться высокопроизводительными вычислениями, чтобы время вычислений резко сократилось.


person gtheo    schedule 15.05.2018    source источник
comment
Здравствуйте, вы можете сначала прочитать документацию по PyCUDA.   -  person YesThatIsMyName    schedule 15.05.2018


Ответы (1)


Извините, читать может быть трудно, но реальность жестока, и многие энтузиасты могут легко испортить man*months усилия по "кодированию" в принципиально -априори проигранная война. Лучше тщательно переоценить все априори известные МИНУСЫ/ПЛЮСы любых планов реинжиниринга, прежде чем вслепую тратить один человеко*день в принципиально неправильном направлении.

Скорее всего, я бы не стал это публиковать. вот, если бы мне не попался Проект, где академики высшего уровня потратили десятки man*years, да, больше года с командой из 12+, на "производство" обработки, занимающей < strong>~ 26 [hr], который воспроизводился менее чем за менее ~ 15 [min] (и намного дешевле с точки зрения затрат на инфраструктуру HPC/GPU), если был разработан с использованием надлежащих (не разрушающих аппаратную производительность) методов проектирования. ..

Не похоже, что я могу поступить иначе.

Ну, на самом деле довольно сложно сказать, если не невозможно:

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

Возможное решение: параллельное программирование

Гораздо проще сказать/напечатать это, чем сделать это на самом деле.

Планирование процесса-желание-использовать-в-истине-[PARALLEL] остается всего лишь желанием и (поверьте мне, или Джину Амдалу, или другим ветеранам К/С, или нет) действительно трудным перепроектированием процесса требуется, если ваш код должен стать значительно лучше, чем в чистом [SERIAL] потоке выполнения кода (как указано выше)

1) чистая [SERIAL] природа fileIO может (почти)убить игру:

неопубликованная часть о чисто-[SERIAL] файловых-доступах (два файла с точками данных)... любой файлIO по своей природе является самым дорогим ресурсом и кроме смарт-ре- разработка чистого [SERIAL] (в лучшем случае единовременной стоимости, но все же) (повторного) чтения в последовательном порядке, поэтому не ожидайте гигантского скачка где-либо далеко от этого в ре -инженерный код. Это будет всегда самая медленная и всегда дорогая фаза.

БОНУС:
Хотя это может показаться наименее привлекательным предметом в списке инвентаря параллельные вычисления, pycuda, распределенные вычисления, hpc, parallelism-amdahl или Что бы ни говорил сленг дальше, элементарная истина заключается в том, что для того, чтобы сделать HPC-вычисления действительно быстрыми и эффективными с точки зрения ресурсов, как входные данные (да, статические файлы), так и вычислительная стратегия обычно оптимизируются для потоковой передачи. -обработки и, чтобы лучше всего, также наслаждаться непрерывным (предотвращением коллизий) локальностью данных, если необходимо достичь максимальной производительности. Любая неэффективность в этих двух областях может не только увеличить, но и фактически ФАКТИРОВАТЬ вычислительные затраты (поэтому РАЗДЕЛИТЬ производительность), и различия могут достигать нескольких порядков ( от [ns] -> [us] -> [ms] -> [s] -> [min] -> [hr] -> [day] -> [week], вы называете их всех... )

2 ) затраты/выгоды могут дать вам ПЛАТИТЕ БОЛЬШЕ, ЧЕМ ВЫ ПОЛУЧАЕТЕ

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

Почему?

GPU-движки — это SIMD-устройства, которые отлично подходят для использования латентной маскировки на обширной области повторяющегося одного и того же блока SMX-инструкций, которым нужен определенный «вес»--»< strong>приятно" - математика должна происходить локально, если они должны показать какое-либо ускорение обработки по сравнению с другими стратегиями реализации задач - GPU-устройства (не геймерские, а HPC, которые не все карты в классе , они? ) лучше всего подходят для действительно небольших областей локальности данных ( матричные операции микроядра, имеющие очень плотный, лучший очень маленький SMX-локальный «RAM» след такого плотного ядра << ~ 100 [kB] по состоянию на 2018/Q2 ).

Ваша «вычислительная» часть кода имеет НУЛЕВОЕ ПОВТОРНОЕ ИСПОЛЬЗОВАНИЕ любого отдельного элемента данных, который был (довольно дорого) извлечен из исходного статического хранилища, поэтому почти все преимущества, которые артиллерия GPU / SMX / SIMD была изобретенный для ВООБЩЕ НЕ ИСПОЛЬЗУЕТСЯ, и вы получаете ОТРИЦАТЕЛЬНУЮ чистую выгоду от попытки загрузить такой код в такие разнородные (сложные NUMA) распределенные вычисления (да, каждое GPU-устройство довольно «далеко», «дорого» (если только ваш код не будет использовать свои SMX-ресурсы до тех пор, пока из кремния GPU не пойдет почти дым…) и «удаленный» асинхронно управляемый узел распределенных вычислений, внутри вашей глобальной вычислительной стратегии) ​​система .

Любое первое ветвление кода графического процессора будет катастрофически дорого с точки зрения затрат на выполнение SIMD, поэтому ваш сильно if код синтаксически корректен, но с точки зрения производительности почти убивает игру:

for i in range( 0,
                len( longitude_aq )
                ): #______________________________________ITERATOR #1 ( SEQ-of-I-s )
    currentAq =                        aq[i, :]           # .SET
    center    = Coordinates(  latitude_aq[i],             # .SET
                             longitude_aq[i]
                             ) #          |
    #                                     +-------------> # EASY2VECTORISE in [i]

    for j in range( 0,
                    len( longitude_meo )
                    ): #- - - - - - - - - - - - - - - - - ITERATOR #2 ( SEQ-of-J-s )
        currentMeo =                        meo[j, :]     # .SET
        grid_point = Coordinates(  latitude_meo[j],       # .SET
                                  longitude_meo[j]
                                  ) #           |
        #                                       +-------> # EASY2VECTORISE in [j]
        if is_in_circle( center,
                         RADIUS,
                         grid_point
                         ): # /\/\/\/\/\/\/\/\/\/\/\/\/\/ IF-ed SIMD-KILLER #1
            if (   currentAq[TIME_AQ]
               == currentMeo[TIME_MEO]
                  ): # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ IF-ed SIMD-KILLER #2
                humidity       += currentMeo[      HUMIDITY_MEO] # BEST PERF.
                pressure       += currentMeo[      PRESSURE_MEO] #   IF SMART
                temperature    += currentMeo[   TEMPERATURE_MEO] #   CURATED
                wind_speed     += currentMeo[    WIND_SPEED_MEO] #   AS NON
                wind_direction += currentMeo[WIND_DIRECTION_MEO] #   ATOMICS
                count          += 1.0

    if          count          != 0.0: # !!!!!!!!!!!!!!!!!! THIS NEVER HAPPENS
        # EXCEPT WHEN ZERO DATA-POINTS WERE AVAILABLE FOR THE i-TH ZONE,
        #        FILE DID NOT CONTAIN ANY SUCH,
        #        WHICH IS FAIR,
        #    BUT SUCH A BLOCK OUGHT NEVER HAVE STARTED ANY COMPUTING AT ALL
        #     IF ASPIRING FOR INDEED BEING LOADED
        #        ONTO AN HPC-GRADE COMPUTING INFRASTRUCTURE ( SPONSORED OR NOT )
        #
        final_tmp[i, HUMIDITY_FINAL]       = humidity       / count
        final_tmp[i, PRESSURE_FINAL]       = pressure       / count
        final_tmp[i, TEMPERATURE_FINAL]    = temperature    / count
        final_tmp[i, WIND_SPEED_FINAL]     = wind_speed     / count
        final_tmp[i, WIND_DIRECTION_FINAL] = wind_direction / count

Если мы опустим итераторы над доменом all-[i,j]-s и перекрестком ifed, фактическая «полезная»-часть вычислений выполняет очень поверхностную математику — работу содержит несколько SLOC-ов, где суммируются принципиально независимые значения (лучше всего избежать каких-либо столкновений операции добавления, поэтому можно было бы очень дешево работать независимо друг от друга (лучше всего с заранее выбранными константами) менее чем за несколько [ns] ДА, ваша вычислительная полезная нагрузка не требует ничего, кроме нескольких единиц [ns] для выполнения.

Проблема заключается в умной инженерии потока данных (мне нравится называть это ГИДРАВЛИКОЙ ДАННЫХ (как сделать дальнейший несжимаемый поток ДАННЫХ в регистры { CPU | GPU | APU | *** }процессора, чтобы получить их обрабатывают))

Все остальное легко. Интеллектуальное решение DATA-HYDRAULICS класса HPC обычно не является таковым.

Никакой язык, никакой фреймворк не поможет вам в этом автоматически. Кто-то может высвободить часть решения-инжиниринга от ваших «ручных» усилий, кто-то не может, кто-то может даже испортить возможную вычислительную производительность из-за «дешевых» сокращений в своем внутреннем проектном решении и сделанных компромиссов, которые не приносят пользы тем же ваша цель – Производительность.


Лучший следующий шаг?

A ) Постарайтесь лучше понять пределы вычислительных инфраструктур, которые вы планируете использовать для своих экстенсивных (но не интенсивных (да, всего несколько SLOC на [i,j] ), которые не нравятся супервайзерам высокопроизводительных вычислений). на свои эксплуатируемые дорогостоящие HPC-ресурсы).

B ) Если у вас есть проблемы со временем, численностью персонала и финансовыми ресурсами для реинжиниринга нисходящего решения DATA-HYDRAULICS, лучше всего реорганизовать свой код, чтобы получить меньше всего в векторизованном, numpy / numba (не всегда numba продвинется значительно дальше, чем уже умный numpy-векторизованный код, но количественный тест покажет факты для каждого инцидента, не в общем)

C ) Если ожидается, что ваша вычислительная проблема будет чаще повторяться, обязательно оцените переработанный конвейер с ранней предварительной обработки хранилища данных (самая медленная часть обработки). , где возможна потоковая предварительная обработка преимущественно статических значений, что может оказать наибольшее влияние на результирующий поток DATA-HYDRAULICS (производительность) с предварительно вычисленными и интеллектуально выровненными значениями. Блок из нескольких ADD вниз по дорожке не улучшится за пределы нескольких [ns], как сообщалось выше, но медленный поток может прыгать на порядки быстрее, если реорганизовать его в разумный поток, используя все доступные, но «просто»-[CONCURRENT]-управляемые ресурсы (любая попытка организовать True-[PARALLEL] планирование здесь полная ерунда, так как задача в принципе ни в коем случае не является проблемой планирования, а является потоком чистой-31_ (повторной) обработки точек данных, где умная, но "просто"-32_ перегруппировка обработки может помогите уменьшить результирующую продолжительность процесса).

БОНУС:
Если вас интересуют более глубокие рассуждения о достижимом приросте производительности за счет перехода на графы вычислений, управляемых N-CPU, не стесняйтесь узнать больше о переформулировал закон Амдала и связанные с ним вопросы, как указано в дополнительных деталях здесь.

person user3666197    schedule 23.10.2019