Когда следует использовать CUDA встроенный warpSize, в отличие от моей собственной постоянной?

Код устройства nvcc имеет доступ к встроенному значению, warpSize, который установлен на размер основы устройства, выполняющего ядро (т.е. 32 в обозримом будущем). Обычно вы не можете отличить его от константы, но если вы попытаетесь объявить массив длины warpSize, вы получите жалобу о том, что он не является константой... (с CUDA 7.5)

Итак, по крайней мере для этой цели вы мотивированы иметь что-то вроде (редактировать):

enum : unsigned int { warp_size  = 32 };

где-то в ваших заголовках. Но теперь - что я должен предпочесть, и когда? : warpSize или warp_size?

Изменить: warpSize по-видимому, является константой времени компиляции в PTX. Тем не менее, вопрос стоит.

-2
источник поделиться
2 ответа

Вопреки ответам на talonmies, я нахожу warp_size constant совершенно приемлемым. Единственная причина использовать warpSize - сделать код вперед-совместимым с возможным будущим оборудованием, которое может иметь искажения разного размера. Однако, когда такое оборудование поступит, код ядра, скорее всего, потребует и других изменений, чтобы оставаться эффективным. CUDA - это не аппаратно-агностический язык - напротив, он все еще довольно низкоуровневый язык программирования. Производственный код использует различные встроенные функции, которые приходят и уходят со временем (например, __umul24).

В тот день, когда мы получим другой размер деформации (например, 64), многое изменится:

  • Значение параметра warpSize должно быть скорректировано
  • Многим встроенным внутренним характеристикам потребуется корректировка подписи или новая версия, например, int __ballot, а в то время как int не должен быть 32-битным, это чаще всего так!
  • Для итерационных операций, таких как сокращения уровня деформации, потребуется их количество итераций. Я никогда не видел, чтобы кто-нибудь писал:

    for (int i = 0; i < log2(warpSize); ++i) ...
    

    что было бы чрезмерно сложным в чем-то, что обычно является критическим по времени фрагментом кода.

  • warpIdx и laneIdx из threadIdx необходимо будет скорректировать. В настоящее время наиболее типичным для меня кодом является:

    warpIdx = threadIdx.x/32;
    laneIdx = threadIdx.x%32;
    

    что сводится к простым действиям смены правой и маской. Однако, если вы замените 32 на warpSize это станет довольно дорогостоящей операцией!

В то же время использование warpSize в коде предотвращает оптимизацию, так как формально это не константа времени компиляции. Кроме того, если количество разделяемой памяти зависит от параметра warpSize это заставляет вас использовать динамически распределенную shmem (согласно ответам talonmies). Однако синтаксис для этого неудобен для использования, особенно когда у вас несколько массивов - это заставляет вас делать арифметику указателей самостоятельно и вручную вычислять сумму всего использования памяти.

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

deviceFunction<warp_size>(params)

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


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

#if __CUDA_ARCH__ <= 600
//all devices of compute capability <= 6.0
static const int warp_size = 32; 
#endif

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

+1
источник

Позвольте получить пару пунктов прямо. Размер основы не является постоянной времени компиляции и не должен рассматриваться как один. Это непосредственная константа времени выполнения, ориентированная на архитектуру (и ее значение как раз составляет 32 для всех архитектур на сегодняшний день). Когда-то старый старомодный компилятор Open64 выдавал константу в PTX, однако это изменилось как минимум 6 лет назад, если моя память не подвела меня.

Значение доступно:

  1. В CUDA C через warpSize, где is не является постоянной времени компиляции (в таких случаях переменная PTX WARP_SZ испускается компилятором).
  2. В ассемблере PTX через WARP_SZ, где это немедленная постоянная времени выполнения
  3. Из API среды выполнения как свойство устройства

Не объявляйте, что вы являетесь константой для размера основы, которая просто требует неприятностей. Обычным вариантом использования для встроенного массива, который может быть несколько кратным размеру основы, будет использование динамически распределенной общей памяти. Вы можете прочитать размер warp из API хоста во время выполнения, чтобы получить его. Если у вас есть статически заявленное ядро, вам нужно измерять размер warp, используйте шаблоны и выберите правильный экземпляр во время выполнения. Последний может показаться ненужным театром, но это правильная вещь для использования, которая практически никогда не возникает на практике. Выбор ваш.

+8
источник

Посмотрите другие вопросы по меткам или Задайте вопрос