Я использовал CMake 3.13 для создания решения Visual Studio (VS 2017) с одним проектом cudatest. Проект состоит из двух файлов:
main.cpp
kernel.cu
Чтобы включить поддержку CUDA, я использовал сценарий, предоставленный Nvidia, то есть FindCUDA.cmake, в отличие от использования последней поддержки CMake для приложений на основе CUDA (я не использую эту последнюю поддержку, потому что она не позволяет мне делать определенные вещи, поэтому Мне нужно прибегнуть к FindCUDA).
CMake успешно сгенерирует проект, содержащий два вышеупомянутых файла. Не вдаваясь в подробности, файл main.cpp содержит объявление функции:
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);
Вы можете распознать это как функцию, которая создается по умолчанию при создании нового проекта в Visual Studio с использованием типа проекта CUDA Runtime (интеграция NVIDIA CUDA для Visual Studio).
В то время как файл kernel.cu содержит определение указанной функции:
__global__ void addKernel(int *c, const int *a, const int *b)
{
int i = threadIdx.x;
c[i] = a[i] + b[i];
}
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)
{
int *dev_a = 0;
int *dev_b = 0;
int *dev_c = 0;
...
addKernel<<<1, size>>>(dev_c, dev_a, dev_b);
...
}
Оба файла успешно скомпилированы, и компоновщик запускает ошибку:
Error LNK2019 unresolved external symbol "enum cudaError __cdecl addWithCuda(int *,int const *,int const *,unsigned int)" ...
Следует отметить, что файл * .obj для kernel.cu успешно сгенерирован NVCC.
Однако файлы * .obj размещены в разных каталогах, что заставляет меня думать, что это может быть проблемой, а также обнаруживает мое непонимание того, где компоновщик Visual Studio ищет файлы * .obj для разрешения символов.
Файл main.obj заканчивается в build\cudatest.dir\Debug, где build - это папка, содержащая сгенерированное решение.
Файл cudatest_generated_kernel.cu.obj попадает в build\CMakeFiles\cudatest.dir\Debug
Конфигурация путей к выходным файлам настраивается скриптом FindCUDA.cmake.
Я попытался поместить cudatest_generated_kernel.cu.obj в ту же папку, что и main.obj, но это ничего не дало.
Установка свойства компоновщика «Показать прогресс» на /VERBOSE показала, что компоновщик даже не ищет cudatest_generated_kernel.cu.obj, чтобы попытаться найти соответствующие символы.
=====================
Вопрос в том:
Учитывая, что я компилирую ядро CUDA в файл * .obj с помощью NVCC, а файл * .cpp в собственный файл * .obj с помощью CL.exe, как я могу сказать компоновщику проверить файл * .obj ядра cuda?
Пожалуйста, дайте мне знать, если я должен расширить вопрос, чтобы сделать его более ясным, т.е. указать параметры компилятора NVCC, которые я использовал, предоставить полный список кода и т. д. Будем очень признательны за любые подсказки и указатели в правильном направлении!
Обновлено:
Согласно предложению пользователя @talonmies (__cdecl addWithCuda может предполагать, что линкер ищет связь C), я явно пометил функцию addWithCuda с помощью extern "C" как в main.cpp, так и в kernel.cu:
extern "C" cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)
Теперь ошибка следующая:
Error LNK2019 unresolved external symbol addWithCuda referenced in function main cudatest D:\projects\vs2017\TestCUDA_CMake\build\main.obj
Отредактировано @talonmies: явная пометка указанной функции с помощью extern "C" аналогичным образом приводит к LNK2019; Спасибо за предложение!





Итак, оказалось, что если проект Visual Studio не имеет интеграции CUDA (проект правой кнопкой мыши -> свойства -> присутствуют свойства компоновщика CUDA C++ / CUDA), ядра CUDA, скомпилированные в файлы * .obj, автоматически не рассматриваются компоновщиком хоста.
Во-первых, я добавил путь к скомпилированным файлам CUDA * .obj в Linker-> Additional Library Directories.
Во-вторых, я указал имя объектного файла в Linker-> Input-> Additional Dependencies.
Это решило проблему. Следует отметить, что изначально я использовал комбинацию нативных команд cmake и макросов FindCUDA.cmake:
add_executable(cudatest main.cpp kernel.cu)
CUDA_WRAP_SRCS(cudatest OBJ generated_files kernel.cu ${cmake_options} OPTIONS ${options} )
Макрос CUDA_WRAP_SRCS добавляет настраиваемые шаги сборки, которые заставляют Visual Studio вызывать nvcc вместо CL для компиляции ядер CUDA. Этот макрос также вызывается из CUDA_ADD_EXECUTABLE и
Макросы CUDA_ADD_LIBRARY, которые также определяют все пути к сгенерированным файлам * .obj, которые уже должны быть включены в сгенерированный проект Visual Studio, что позволяет сэкономить дополнительные усилия, которые я описал выше.
Кроме того, CUDA_ADD_EXECUTABLE и CUDA_ADD_LIBRARY также облегчают создание проектов для ядер, в которых включен код перемещаемого устройства, что означает, что должен быть этап предварительной привязки, который будет связывать все файлы CUDA * .obj с кодом перемещаемого устройства в них в промежуточные Файл CUDA * .obj, который может быть использован компоновщиком хоста (иначе компоновщик хоста не сможет обрабатывать файлы * .obj, содержащие код перемещаемого устройства). Вышеупомянутые макросы также будут включать зависимости от этого промежуточного сгенерированного файла CUDA * .obj.
Это похоже на проблему искажения символа.
__cdecl addWithCuda(предполагает, что компоновщик ищет связь C, а не связь C++