Почему эффективность работы желательна в программировании на GPU?

13

Я читал следующую статью о том, как сделать параллельное сканирование в CUDA:

https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch39.html

В статье делается акцент на том, чтобы сделать сканирование «эффективным». Другими словами, алгоритм GPU должен выполнять не больше дополнений, чем алгоритм CPU, O (n). Авторы представляют два алгоритма, один «наивный», который делает O (nlogn) дополнения, и один, который они считают «эффективным». Однако эффективный алгоритм работы выполняет вдвое больше итераций цикла.

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

Mokosha
источник

Ответы:

21

Прежде всего, повторяйте: «GPU - это просто гигантские процессоры SIMD, и они должны работать в режиме блокировки», это немного сложнее. Весь GPU не работает синхронно. Потоки шейдеров организованы в группы из 32, называемых «перекосами» (на NVIDIA; на AMD это группы по 64, называемые «волновыми фронтами», но с той же концепцией). Внутри деформации все потоки работают в режиме ожидания как массив SIMD. Однако разные перекосы не идут в ногу друг с другом. Кроме того, некоторые деформации могут активно выполняться, в то время как другие могут быть приостановлены, так же, как потоки процессора. Деформации могут быть приостановлены либо потому, что они чего-то ждут (например, транзакции памяти для возврата или очистки барьеров), либо потому, что нет '

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

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

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

    Рассмотрим количество активных потоков в наивном алгоритме. Посмотрев на рисунок 2 в статье, вы можете увидеть, что все потоки активны, за исключением первых 2 k на k- й итерации. Таким образом, для N потоков количество активных потоков равно N - 2 k . Например, при N = 1024 число активных потоков за итерацию равно:

    1023, 1022, 1020, 1016, 1008, 992, 960, 896, 768, 512
    

    Если я преобразую это в число активных деформаций (путем деления на 32 и округления вверх), я получу:

    32, 32, 32, 32, 32, 31, 30, 28, 24, 16
    

    на сумму 289. С другой стороны, эффективный алгоритм запускается с вдвое меньшим количеством потоков, затем он вдвое сокращает количество активных на каждой итерации до тех пор, пока не опустится до 1, а затем начнет удваиваться, пока не вернется к снова половина размера массива:

     512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512
    

    Преобразование этого в активные деформации:

    16, 8, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 8, 16
    

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

При всем при этом, к сожалению, статья GPU Gems не дает четкого объяснения всему этому, вместо этого сосредоточив внимание на анализе «большого числа дополнений», который, хотя и не является абсолютно неуместным, пропускает множество деталей о том, почему этот алгоритм Быстрее.

Натан Рид
источник