CUDA определяет потоки на блок, блоки на каждую сетку

Я новичок в парадигме CUDA. Мой вопрос заключается в определении количества потоков на блок и блоков в сетке. Входит ли в это искусство и испытание? Я обнаружил, что многие примеры имеют, по-видимому, произвольное число, выбранное для этих вещей.

Я рассматриваю проблему, когда я мог бы передавать матрицы - любого размера - методу умножения. Таким образом, каждый элемент C (как в C = A * B) будет вычисляться одним потоком. Как бы вы определили потоки/блок, блоки/сетку в этом случае?

Ответ 1

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

Графический процессор с поддержкой CUDA имеет свои возможности обработки, разделенные на SM (потоковые мультипроцессоры), а количество SM зависит от реальной карты, но здесь мы сосредоточимся на одном SM для простоты (все они ведут себя одинаково), Каждый SM имеет конечное число 32-битных регистров, разделяемую память, максимальное количество активных блоков и максимальное количество активных потоков. Эти числа зависят от CC (вычислительная способность) вашего GPU и могут быть найдены в середине статьи Wikipedia http://en.wikipedia.org/wiki/CUDA.

Прежде всего, размер блока потока всегда должен быть кратным 32, потому что ядра выдают инструкции в warps (32 потока). Например, если у вас размер блока 50 потоков, GPU по-прежнему выдаст команды для 64 потоков, и вы просто будете их расточать.

Во-вторых, прежде чем беспокоиться об общей памяти и регистрах, попробуйте размер блоков на основе максимального количества потоков и блоков, соответствующих вычислительной способности вашей карты. Иногда есть несколько способов сделать это... например, карта CC 3.0 каждый SM может иметь 16 активных блоков и 2048 активных потоков. Это означает, что если у вас есть 128 потоков на блок, вы можете поместить 16 блоков в свой SM, прежде чем достигнуть предела потока 2048. Если вы используете 256 потоков, вы можете поместиться только в 8, но вы все еще используете все доступные потоки и по-прежнему будете иметь полное заполнение. Однако при использовании 64 потоков на блок будет использоваться только 1024 потока при ударе по 16 блокам, поэтому только 50% заполняемости. Если разделяемая память и использование регистров не являются узким местом, это должно быть вашей главной проблемой (кроме ваших размеров данных).

По теме вашей сетки... блоки в вашей сетке распределяются по SM для запуска, а затем остальные блоки помещаются в конвейер. Блоки перемещаются в SM для обработки, как только в этом SM достаточно ресурсов, чтобы взять блок. Другими словами, когда блоки завершены в SM, новые перемещаются. Вы можете сделать аргумент о том, что меньшие блоки (128 вместо 256 в предыдущем примере) могут завершиться быстрее, поскольку особенно медленный блок будет обрабатывать меньше ресурсов, но это очень сильно зависит от кода.

Что касается регистров и разделяемой памяти, посмотрите на следующее, так как это может ограничивать ваше размещение. Общая память ограничена для всего SM, поэтому попробуйте использовать ее в количестве, которое позволяет как можно больше блоков по-прежнему поместиться на SM. То же самое касается использования регистра. Опять же, эти числа зависят от вычислительной способности и могут быть найдены в таблице на странице wikipedia. Удачи!

Ответ 2

https://docs.nvidia.com/cuda/cuda-occupancy-calculator/index.html

  CUDA Occupancy Calculator позволяет вам вычислить многопроцессорную занятость графического процессора данным ядром CUDA. Занятость мультипроцессора - это отношение активных деформаций к максимальному количеству деформаций, поддерживаемых в многопроцессорных графических процессорах. Каждый мультипроцессор на устройстве имеет набор из N регистров, доступных для использования потоками программы CUDA. Эти регистры являются общим ресурсом, который распределяется среди потоковых блоков, выполняемых на многопроцессорном компьютере. Компилятор CUDA пытается минимизировать использование регистров, чтобы максимизировать количество блоков потоков, которые могут быть активны в машине одновременно. Если программа пытается запустить ядро, для которого количество регистров, используемых на поток, превышает размер блока потока больше N, запуск завершится неудачно...

Ответ 3

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

Выбор количества потоков на каждый блок очень сложный. Большинство алгоритмов CUDA допускают широкий диапазон возможностей, и выбор основан на том, что делает ядро ​​работать наиболее эффективно. Это почти всегда кратно 32 и по меньшей мере 64 из-за того, как работает оборудование планирования потоков. Хорошим выбором для первой попытки является 128 или 256.

Ответ 4

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

Например, с точки зрения переключения контекста любое число из 32 работает одинаково. Таким образом, для случая 1D запуск 1 блока с 64 потоками или 2 блока с 32 потоками не имеет никакого значения для доступа к глобальной памяти. Однако, если проблема под рукой, естественно, разлагается на один вектор длиной 64, тогда первый вариант будет лучше (меньше накладных расходов памяти, каждый поток может получить доступ к одной и той же общей памяти), чем второй.