CUDA: как использовать -arch и -code и SM против COMPUTE

Я до сих пор не уверен, как правильно указать архитектуры для генерации кода при сборке с помощью nvcc. Я знаю, что в мой двоичный файл встроен машинный код, а также код PTX, и что им можно управлять с помощью переключателей контроллера -code и -arch (или их комбинации с помощью -gencode).

Теперь, согласно this, кроме двух там флагов компилятора также есть два способа определения архитектур: sm_XX и compute_XX, где compute_XX относится к виртуальной и sm_XX к реальной архитектуре. Флаг -arch принимает только идентификаторы для виртуальных архитектур (например, compute_XX), тогда как флаг -code принимает и идентификаторы для реальной, и для виртуальной архитектуры.

В документации указано, что -arch определяет виртуальные архитектуры, для которых скомпилированы входные файлы. Однако этот код PTX не компилируется автоматически в машинный код, это скорее «этап предварительной обработки».

Теперь -code должен указывать, для каких архитектур код PTX собран и оптимизирован.

Однако неясно, какой PTX или двоичный код будет встроен в двоичный файл. Если я укажу, например, -arch=compute_30 -code=sm_52, означает ли это, что мой код сначала будет скомпилирован в PTX уровня функций 3.0, из которого впоследствии будет создан машинный код для уровня функций 5.2? А что будет встроено?

Если я просто укажу -code=sm_52, что тогда будет? Будет встроен только машинный код для V5.2, созданный из кода PTX V5.2? А в чем будет разница с -code=compute_52?


person bweber    schedule 26.02.2016    source источник


Ответы (1)


Некоторые связанные вопросы / ответы: здесь и здесь.

Я до сих пор не уверен, как правильно указать архитектуры для генерации кода при сборке с помощью nvcc.

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

-gencode arch=compute_XX,code=sm_XX

где XX - это двузначная вычислительная мощность графического процессора, на который вы хотите ориентироваться. Если вы хотите настроить таргетинг на несколько графических процессоров, просто повторите всю последовательность для каждой цели XX. Это примерно тот же подход, который используется в проектах примеров кода CUDA. (Если вы хотите включить PTX в свой исполняемый файл, включите дополнительный -gencode с параметром code, указав ту же виртуальную архитектуру PTX, что и параметр arch).

Еще одна довольно простая форма, нацеленная только на один графический процессор, - это просто использовать:

-arch=sm_XX 

с таким же описанием для XX. Эта форма будет включать как SASS, так и PTX для указанной архитектуры.

Теперь, согласно этому, помимо двух флагов компилятора, есть также два способа указания архитектур: sm_XX и compute_XX, где compute_XX относится к виртуальной, а sm_XX - к реальной архитектуре. Флаг -arch принимает только идентификаторы для виртуальных архитектур (например, compute_XX), тогда как флаг -code принимает и идентификаторы для реальной, и для виртуальной архитектуры.

Это в основном правильно, когда arch и code используются в качестве суб-переключателей в -gencode переключателе или если оба используются вместе, как вы описываете автономно. Но, например, когда -arch используется сам по себе (без -code), он представляет собой другой вид «сокращенной» записи, и в этом случае вы можете передать реальную архитектуру, например -arch=sm_52

Однако неясно, какой PTX или двоичный код будет встроен в двоичный файл. Если я укажу, например, -arch = compute_30 -code = sm_52, означает ли это, что мой код сначала будет скомпилирован до уровня функций 3.0 PTX, из которого впоследствии будет создан машинный код для уровня функций 5.2? А что будет встроено?

Точное определение того, что внедряется, зависит от формы использования. Но для этого примера:

-gencode arch=compute_30,code=sm_52

или для аналогичного случая, который вы укажете:

-arch=compute_30 -code=sm_52

тогда да, это значит, что:

  1. Временный код PTX будет сгенерирован из вашего исходного кода, и он будет использовать cc3.0 PTX.
  2. Из этого PTX инструмент ptxas сгенерирует код SASS, совместимый с cc5.2.
  3. Код SASS будет встроен в ваш исполняемый файл.
  4. Код PTX будет удален.

(Я не уверен, почему вы на самом деле указали такую ​​комбинацию, но это законно.)

Если я просто укажу -code = sm_52, что тогда будет? Будет встроен только машинный код для V5.2, созданный из кода PTX V5.2? И в чем будет разница для -code = compute_52?

-code=sm_52 будет генерировать код SASS cc5.2 из промежуточного кода PTX. Код SASS будет встроен, PTX будет удален. Обратите внимание, что указание этой опции отдельно в этой форме, без опции -arch, было бы незаконным. (1)

-code=compute_52 сгенерирует код PTX cc5.x (только) и встроит этот PTX в исполняемый / двоичный файл. Обратите внимание, что указание этой опции отдельно в этой форме, без опции -arch, было бы незаконным. (1)

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

(1) Когда не используется переключатель -gencode и не используется переключатель -arch, nvcc предполагает, что к вашей команде компиляции добавлено значение по умолчанию -arch=sm_20 (это для CUDA 7.5, настройка -arch по умолчанию может отличаться в зависимости от версии CUDA). sm_20 - это настоящая архитектура, и недопустимо указывать настоящую архитектуру для параметра -arch, если также указан параметр -code.

person Robert Crovella    schedule 26.02.2016
comment
Когда я впервые попытался прояснить, как лучше всего использовать эти флаги, я использовал cuobjdump --dump-ptx и cuobjdump --dump-sass, чтобы проверить, какие версии SASS (двоичный машинный код) и PTX (независимый от промежуточной архитектуры код) отправляются в объектный файл. Я бы рекомендовал этот практический подход другим. На практике, чтобы сгенерировать исполняемый файл с поддержкой нескольких архитектур, вам нужно включить SASS для каждой из этих архитектур, плюс PTX только для самой последней архитектуры (для JIT-компиляции на потенциальных будущих графических процессорах) . - person njuffa; 26.02.2016
comment
Хорошо, я правильно понимаю: если бы я хотел встроить машинный код для всех существующих архитектур и код PTX только для последних вычислительных возможностей, я мог бы сделать что-то вроде этого: -gencode arch=compute_20,code=sm_20 [...] -gencode arch=compute_53,code=sm_53 -gencode arch=compute_53,code=compute_53? Так что только -code влияет на то, что будет встроено !? - person bweber; 28.02.2016
comment
Да, примерно так должно работать. Как я уже сказал, это более или менее то, что делают образцы проектов. Возможно, вы захотите взглянуть на то, как настроены эти make-файлы или VS-проекты. - person Robert Crovella; 28.02.2016
comment
Есть ли конфликт при компиляции с мультигенкодом? например, половинная функция __hmul поддерживается только после вычисления 5.2. Если nvcc компилируется с мульти-генкодом, например 3.5,5.2,7.0, возникнет конфликт из-за поддержки половинной точности для разных арок. - person Xiaolin Wu; 29.12.2019
comment
Вы не можете скомпилировать код для арки, которая не поддерживает какую-либо функцию, которую вы используете. - person Robert Crovella; 29.12.2019
comment
@XiaolinWu вы можете, если вы обусловите свою компиляцию значением автоматически определенного макроса __CUDA_ARCH__. В вашем примере код будет предварительно обработан три раза с указанным выше значением макроса 350, 520 и 700 (арка × 100) соответственно. То, что вы делаете, например, #if __CUDA_ARCH__ >= 520 (код с использованием _hmul), #else (что-то разумное: сообщить об ошибке или выполнить вычисления менее эффективно) #endif. См. Документацию в docs. nvidia.com/cuda/cuda-compiler-driver-nvcc/ - person kkm; 11.10.2020