Как узнать, подходит ли моя смущающая параллельная задача для графического процессора? - PullRequest
0 голосов
/ 26 апреля 2018

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

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

import numpy as np

from numba import guvectorize

@guvectorize(['void(float64[:], intp[:], float64[:])'], '(n),()->(n)')
def move_mean(a, window_arr, out):
    window_width = window_arr[0]
    asum = 0.0
    count = 0
    for i in range(window_width):
        asum += a[i]
        count += 1
        out[i] = asum / count
    for i in range(window_width, len(a)):
        asum += a[i] - a[i - window_width]
        out[i] = asum / count

arr = np.arange(2000000, dtype=np.float64).reshape(200000, 10)
print(arr)
print(move_mean(arr, 3))

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

Я пытался использовать guVectorize в библиотеке Numba, чтобы назначить его на графический процессор Nvidia. Это работает нормально, но я не получаю ускорение.

Подходит ли этот тип задач для графического процессора в принципе? то есть, если я углублюсь в Numba и начну настраивать потоки, блоки и управление памятью или реализацию алгоритма, то теоретически я смогу ускориться. Или эта проблема в принципе просто не подходит для архитектуры.

Ответы ниже, кажется, предполагают, что это не подходит, но я еще не совсем убежден.

numba - guvectorize чуть быстрее, чем jit

И numba guvectorize target = «параллель» медленнее, чем target = «процессор»

1 Ответ

0 голосов
/ 27 апреля 2018

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

Давайте посмотрим на общую конфигурацию и немного посчитаем:

  1. Пропускная способность памяти CPU-RAM составляет ок. 24 ГБ / с
  2. пропускная способность CPU-GPU составляет ок. 8GB / s
  3. Пропускная способность памяти GPU-RAM составляет ок. 180GB / с

Давайте предположим, что нам нужно передать 24 ГБ данных для выполнения задачи, поэтому у нас будет следующее оптимальное время (вопрос о том, как и как достичь этого времени - другой вопрос!):

  1. сценарий: только время процессора = 24 ГБ / 24 ГБ / с = 1 секунда.
  2. : данные должны передаваться из ЦП в графический процессор (24 ГБ / 8 ГБ / с = 3 секунды) и обрабатываться там (24 ГБ / 180 ГБ / с = 0,13 секунды), что приводит к 3,1 секунды.
  3. : данные уже находятся на устройстве, поэтому требуется только 24 ГБ / 180 ГБ / с = 0,13 секунды.

Как видно, существует потенциал для ускорения, но только в 3. сценарии - когда ваши данные уже находятся на GPU-устройстве.

Однако достижение максимальной пропускной способности является довольно сложной задачей.

Например, при обработке матрицы по строкам на ЦП вы хотели бы, чтобы ваши данные были в главном порядке строк (C-порядке), чтобы получить максимальную отдачу от L1-кэша: while читая двойное число, вы фактически загружаете 8 двойных в кеш, и вы не хотите, чтобы они были удалены из кеша, прежде чем вы сможете обработать оставшиеся 7.

В GPU, с другой стороны, вы хотите, чтобы обращения к памяти были объединенными , например. поток 0 должен иметь доступ к адресу 0, поток 1 - адрес 1 и так далее. Чтобы это работало, данные должны располагаться в мажорном столбце (Fortran-порядок).


Есть еще одна вещь, которую нужно учитывать: способ тестирования производительности. Ваш тестовый массив имеет размер всего около 2 МБ и, следовательно, достаточно мал для кэша L3. Пропускная способность кэша L3 зависит от количества ядер, используемых для расчета, но будет, по крайней мере, около 100 ГБ / с - не намного медленнее, чем GPU, и, вероятно, намного быстрее при распараллеливании на CPU.

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


Несколько неуместное замечание: ваш алгоритм не очень устойчив с цифровой точки зрения.

Если ширина окна была 3, как в вашем примере, но в строке было около 10**4 элементов. Таким образом, для последнего элемента значение является результатом примерно 10**4 сложений и вычитаний, каждое из которых добавляет к значению ошибку округления - по сравнению только с тремя тремя сложениями, если они сделаны "наивно", это большая разница.

Конечно, это может не иметь значения (для 10 элементов подряд, как в вашем примере), но также может укусить вас однажды ...

...