Создание условных Makefiles с помощью разных компиляторов, C++ и CUDA

Я пытался создать Makefile, который делает следующее. У меня есть C++ программа, которая вызывает CUDA функции, поэтому у меня есть набор .cpp и .cu файлов со всеми CUDA ядрами в .cu файлах.

Для каждого вызова ядра у меня также есть версия этой функции для процессора, поэтому в моих файлах .cpp есть код, который выглядит так:

if (with_cuda)
{
    call_kernel(parameters);
}
else
{
    call_cpu_function(parameters);
}

Функция call_kernel определена в файлах .cu, а затем вызывает фактическое ядро ​​kernel<<<X,X>>>(...).

Я хочу иметь возможность скомпилировать свою программу в системе без набора инструментов GPU или CUDA. Я могу легко инкапсулировать функции, которые будут вызывать ядра, с помощью некоторого макроса, например, приведенный выше код может выглядеть так:

if (with_cuda)
{
#ifdef WITH_CUDA
    call_kernel(parameters);
#endif
}
else
{
    call_cpu_function(parameters);
}

А потом скомпилировать либо g++ files.cpp без поддержки CUDA, либо nvcc files.cpp other_files.cu -DWITH_CUDA

И я должен инкапсулировать все остальные включения в CUDA библиотеки и так далее с помощью того же макроса #ifdef...

Как мне сделать Makefile, который ищет установленный набор инструментов CUDA, а затем выполняет компиляцию с nvcc или с g++, если он не установлен? Я рассматривал возможность написания Makefile напрямую, используя cmake, или autoconf с помощью скрипта configure, но я совершенно потерян, поэтому я готов использовать любой другой метод, который может быть предпочтительнее.

Кроме того, если эта макроинкапсуляция каждого вызова ядра не является стандартным или лучшим способом сделать такого рода вещи, я также буду рад изменить свой подход к этому. Я вижу потенциальную проблему, связанную с необходимостью выполнять оператор if для каждого вызова, как в приведенном выше коде, помимо макроса ifdef, поэтому я мог бы скомпилировать без CUDA. Тем не менее, по какой-то причине для логического значения with_cuda установлено значение true, и функция не вызывается, так что я понимаю, почему это не может быть хорошим решением.

Выполнение этого с помощью CMake должно быть относительно простым, см. команду FindCUDAToolkit, которая заполняет CUDAToolkit_FOUND.

paleonix 09.04.2023 00:05

make-файлы, которые поставляются с образцами CUDA, могут представлять интерес. Я не предлагаю делать это именно так, но у них достаточно условных рамок, чтобы, вероятно, дать вам некоторые идеи.

Robert Crovella 09.04.2023 04:50
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

Итак, когда в вашем файле C++ у вас есть:

do_stuff(params);

call - предоставить только объявление этой функции, но не определение.

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

  • Скомпилируйте файл .cu с ядром и оболочкой do_stuff() на стороне хоста.
  • Скомпилируйте файл .cpp с реализацией do_stuff() на базе процессора.

... и связать объектный файл с вызовами do_stuff() либо с одной реализацией, либо с другой.

Вот как вы могли бы сделать это с помощью CMake: вы записываете этот CMakeLists.txt файл в каталог вашего проекта:

cmake_minimum_required(VERSION 3.8)
include(CheckLanguage)

project(myapp LANGUAGES CXX)

add_executable(myapp calls_dostuff.cpp) 

check_language(CUDA)
if (CMAKE_CUDA_COMPILER)
    enable_language(CUDA)
    add_dependencies(myapp dostuff_impl_calling_gpu_kernel.cu) 
else()
    add_dependencies(myapp dostuff_cpu_impl.cpp) 
endif ()

... и затем вызвать:

cmake -B my_build_path -S project_dir_path -G "Unix Makefiles"

чтобы сгенерировать Makefile в my_build_path для проекта, находящегося в project_dir_path.

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

MyUserIsThis 09.04.2023 22:55

Немного хакерский, но:

ifeq ($(call,$(shell which nvcc))$(.SHELLSTATUS),0)
# Have NVCC
else
# No NVCC
endif

Это запускает which nvcc и проверяет, находит ли он какой-либо исполняемый файл.

$(shell ) запускает команду оболочки и возвращает все, что она печатает, на стандартный вывод. $(call, ) — это хакерский способ отбросить это возвращаемое значение, так как оно нам ни для чего не нужно, и нас интересует только код выхода. $(.SHELLSTATUS) возвращает код выхода последнего $(shell ) вызова. ifeq затем сравнивает его на равенство с 0.

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

MyUserIsThis 09.04.2023 22:55

Другие вопросы по теме