Я пытался создать 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, и функция не вызывается, так что я понимаю, почему это не может быть хорошим решением.
make-файлы, которые поставляются с образцами CUDA, могут представлять интерес. Я не предлагаю делать это именно так, но у них достаточно условных рамок, чтобы, вероятно, дать вам некоторые идеи.
Вам не нужно прыгать через все эти обручи, такие как условная компиляция одного и того же файла с помощью разных компиляторов или прописывание условия 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
.
Спасибо! Это кажется гораздо более профессиональным и менее подверженным ошибкам, чем то, что я делал.
Немного хакерский, но:
ifeq ($(call,$(shell which nvcc))$(.SHELLSTATUS),0)
# Have NVCC
else
# No NVCC
endif
Это запускает which nvcc
и проверяет, находит ли он какой-либо исполняемый файл.
$(shell )
запускает команду оболочки и возвращает все, что она печатает, на стандартный вывод. $(call, )
— это хакерский способ отбросить это возвращаемое значение, так как оно нам ни для чего не нужно, и нас интересует только код выхода. $(.SHELLSTATUS)
возвращает код выхода последнего $(shell )
вызова. ifeq
затем сравнивает его на равенство с 0
.
Спасибо за ваш ответ, ответ einpoklum кажется лучше, но я мог бы использовать этот более хакерский метод для небольших проектов.
Выполнение этого с помощью CMake должно быть относительно простым, см. команду FindCUDAToolkit, которая заполняет
CUDAToolkit_FOUND
.