CUDA 5.0 отдельная компиляция библиотеки с cmake
Время сборки моей библиотеки cuda увеличивается, поэтому я подумал, что отдельная компиляция, представленная в CUDA 5.0, может мне помочь. Я не мог понять, как добиться отдельной компиляции с помощью cmake. Я просмотрел документацию NVCC и нашел, как скомпилировать объект устройства (с использованием опции -dc) и как связать их (используя -dlink). Мои попытки запустить его с помощью cmake не удались. Я использую cmake 2.8.10.2 и голову ствола FindCUDA.cmake. Однако я не мог узнать, как указать, какие файлы должны быть скомпилированы, и как связать их с библиотекой.
Особенно синтаксис function(CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS output_file_var cuda_target options object_files source_files)
неясен для меня, потому что я не знаю, что такое output_file_var
и cuda_target
.
Здесь не работают результаты моих попыток:
cuda_compile(DEVICEMANAGER_O devicemanager.cu OPTIONS -dc)
cuda_compile(BLUB_O blub.cu OPTIONS -dc)
CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS(TEST_O gpuacceleration
"" DEVICEMANGER_O BLUB_O)
set(LIB_TYPE SHARED)
#cuda_add_library(gpuacceleration ${LIB_TYPE}
#${gpuacc_SRCS}
#devicemanager.cu
# blub.cu
#DEVICEMANAGER_O
# TEST_O
#)
Кто-нибудь знает, как скомпилировать и связать библиотеку cuda с помощью cmake?
Спасибо заранее.
EDIT:
После того, как друг посоветовался с разработчиком FindCUDA.cmake, ошибка была исправлена в примере с FindCUDA.cmake(https://gforge.sci.utah.edu/gf/project/findcuda/scmsvn/?action=browse&path=%2F checkout% 2Ftrunk% 2FFindCuda.html),
Теперь я могу построить пример.
В моем проекте я могу построить библиотеку по мере необходимости, используя следующую (см. 2.8.10):
set(LIB_TYPE SHARED)
set(CUDA_SEPARABLE_COMPILATION ON)
cuda_add_library(gpuacceleration ${LIB_TYPE}
blub.cu
blab.cu
)
НО:
Я не могу ссылаться на эту библиотеку. Когда я построил lib без отдельной компиляции, я смог связать ее.
Теперь получаем следующую ошибку:
undefined reference to `__cudaRegisterLinkedBinary_53_tmpxft_00005ab4_00000000_6_blub_cpp1_ii_d07d5695'
для каждого файла с функцией, используемой в интерфейсе. Кажется странным, поскольку он строит без предупреждения и т.д.
Любые идеи, как заставить это работать?
EDIT:
Наконец я понял, как это сделать. См. @PHD и мой ответ для деталей.
Ответы
Ответ 1
Наконец-то я его запустил;)
В дополнение к ответу @PHD и моим комментариям к нему я изменил: set(BUILD_SHARED_LIBS OFF)
в моем CMakeLists.txt
, поскольку разделяемые библиотеки не поддерживаются для отдельной компиляции в соответствии с nvcc вручную v5.0 стр. 40.
В дополнение к этому используйте последний rev (1223) из репозитория вместо rev 1221. Я связался с разработчиком, и он исправил некоторые проблемы, блокирующие это.
Эта ревизия не устанавливает флаг nvcc
-arch=sm_xx
правильно, поэтому я добавил это вручную для своего проекта и сообщил разработчику FindCUDA.cmake. Таким образом, это может быть исправлено в будущем.
Не забудьте получить cmake > 2.8.10 для этого.
Надеюсь, это поможет никому, кроме меня;)
Вот мой CMakeLists.txt:
#Required for CUDA-Check
cmake_minimum_required(VERSION 2.8.10)
project(gpulib)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake/cuda" ${CMAKE_MODULE_PATH})
# ============================================
# === Target
# ============================================
file(GLOB_RECURSE gpuacc_SRCS "*.cu")
include_directories(.)
# ---------------------------------------
# Find Cuda
find_package(CUDA REQUIRED)
set(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE ON)
set(BUILD_SHARED_LIBS OFF)
set(CUDA_SEPARABLE_COMPILATION ON)
#list(APPEND CUDA_NVCC_FLAGS -arch=sm_20)
set(LIB_NAME "gpuacceleration")
cuda_add_library(${LIB_NAME}
${gpuacc_SRCS}
OPTIONS -DSTUFF="blah blah"
RELEASE -DNDEBUG
DEBUG -g -DDEBUG
)
set(PUBLIC_HEADERS "myheader1.h;myheader2.h")
INSTALL(FILES ${PUBLIC_HEADERS} DESTINATION include)
INSTALL(FILES "${CMAKE_BINARY_DIR}/src/lib${LIB_NAME}.a" DESTINATION lib)
EDIT: это не работает!
Проблема заключается в том, что undefined ссылается на все функции cuda (например, cudaMalloc) при связывании сгенерированной библиотеки при создании исполняемого файла в основном проекте.
По-прежнему работает над ним
Ответ 2
EDIT (2016-03-15): Да, это подтверждается как ошибка в FindCUDA: https://cmake.org/Bug/view.php?id=15157
TL; DR: Кажется, это ошибка в FindCUDA, которая делает объекты незаметными для внешних определений до окончательной ссылки.
Проблема заключается в том, что даже если разделяемая компиляция включена, шаг привязки по-прежнему выполняется для всех целей индивидуально до окончательной компоновки.
Например, у меня есть module.cu
с:
#include "module.h"
#include <cstdio>
double arr[10] = {1,2,3,4,5,6,7,8,9,10};
__constant__ double carr[10];
void init_carr() {
cudaMemcpyToSymbol(carr,arr,10*sizeof(double));
}
__global__ void pkernel() {
printf("(pkernel) carr[%d]=%g\n",threadIdx.x,carr[threadIdx.x]);
}
void print_carr() {
printf("in print_carr\n");
pkernel<<<1,10>>>();
}
и module.h
с:
extern __constant__ double carr[10];
extern double arr[10];
void print_carr();
void init_carr();
и, наконец, main.cu
с:
#include "module.h"
#include <cstdio>
__global__ void kernel() {
printf("(kernel) carr[%d]=%g\n",threadIdx.x,carr[threadIdx.x]);
}
int main(int argc, char *argv[]) {
printf("arr: %g %g %g ..\n",arr[0],arr[1],arr[2]);
kernel<<<1,10>>>();
cudaDeviceSynchronize();
print_carr();
cudaDeviceSynchronize();
init_carr();
cudaDeviceSynchronize();
kernel<<<1,10>>>();
cudaDeviceSynchronize();
print_carr();
cudaDeviceSynchronize();
return 0;
}
Затем это отлично работает со следующим Makefile
:
NVCC=nvcc
NVCCFLAGS=-arch=sm_20
LIB=libmodule.a
OBJS=module.o main.o
PROG=extern
$(PROG): main.o libmodule.a
$(NVCC) $(NVCCFLAGS) -o [email protected] $^
%.o: %.cu
$(NVCC) $(NVCCFLAGS) -dc -c -o [email protected] $^
$(LIB): module.o
ar cr [email protected] $^
clean:
$(RM) $(PROG) $(OBJS) $(LIB)
Но затем я пытаюсь использовать следующие CMakeLists.txt
:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8)
PROJECT(extern)
FIND_PACKAGE(CUDA REQUIRED)
SET(CUDA_SEPARABLE_COMPILATION ON)
SITE_NAME(HOSTNAME)
SET(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -arch=sm_20)
cuda_add_library(module module.cu)
CUDA_ADD_EXECUTABLE(extern main.cu)
TARGET_LINK_LIBRARIES(extern module)
При компиляции, что происходит, это следующее:
$ cmake ..
-- The C compiler identification is GNU 4.9.2
...
$ make VERBOSE=1
...
[ 25%] Building NVCC (Device) object CMakeFiles/module.dir//./module_generated_module.cu.o
...
-- Generating <...>/build/CMakeFiles/module.dir//./module_generated_module.cu.o
/usr/local/cuda/bin/nvcc <...>/module.cu -dc -o <...>/build/CMakeFiles/module.dir//./module_generated_module.cu.o -ccbin /usr/bin/cc -m64 -Xcompiler ,\"-g\" -arch=sm_20 -DNVCC -I/usr/local/cuda/include
[ 50%] Building NVCC intermediate link file CMakeFiles/module.dir/./module_intermediate_link.o
/usr/local/cuda/bin/nvcc -arch=sm_20 -m64 -ccbin "/usr/bin/cc" -dlink <...>/build/CMakeFiles/module.dir//./module_generated_module.cu.o -o <...>/build/CMakeFiles/module.dir/./module_intermediate_link.o
...
/usr/bin/ar cr libmodule.a CMakeFiles/module.dir/./module_generated_module.cu.o CMakeFiles/module.dir/./module_intermediate_link.o
/usr/bin/ranlib libmodule.a
...
[ 50%] Built target module
[ 75%] Building NVCC (Device) object CMakeFiles/extern.dir//./extern_generated_main.cu.o
...
-- Generating <...>/build/CMakeFiles/extern.dir//./extern_generated_main.cu.o
/usr/local/cuda/bin/nvcc <...>/main.cu -dc -o <...>/build/CMakeFiles/extern.dir//./extern_generated_main.cu.o -ccbin /usr/bin/cc -m64 -Xcompiler ,\"-g\" -arch=sm_20 -DNVCC -I/usr/local/cuda/include -I/usr/local/cuda/include
...
[100%] Building NVCC intermediate link file CMakeFiles/extern.dir/./extern_intermediate_link.o
/usr/local/cuda/bin/nvcc -arch=sm_20 -m64 -ccbin "/usr/bin/cc" -dlink <...>/build/CMakeFiles/extern.dir//./extern_generated_main.cu.o -o <...>/build/CMakeFiles/extern.dir/./extern_intermediate_link.o
nvlink error : Undefined reference to 'carr' in '<...>/build/CMakeFiles/extern.dir//./extern_generated_main.cu.o'
Ясно, что проблема - это строки nvcc -dlink obj.o -o obj_intermediate_link.o
. Тогда, я думаю, информация о внешних определениях теряется. Итак, вопрос в том, можно ли заставить CMake/FindCUDA не выполнять этот дополнительный шаг привязки?
В противном случае я бы сказал, что это ошибка. Вы согласны? Я могу отправить отчет об ошибке с помощью CMake.
Ответ 3
Протестировано с версией nvcc:
nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2012 NVIDIA
Corporation Built on Fri_Sep_21_17:28:58_PDT_2012 Cuda compilation
tools, release 5.0, V0.2.1221
и svn:
URL: https://gforge.sci.utah.edu/svn/findcuda/trunk
Repository Root: https://gforge.sci.utah.edu/svn/findcuda
Repository UUID: 81322f20-870f-0410-845c-a4cd4664c908
Revision: 1221
Node Kind: directory
Schedule: normal
Last Changed Rev: 1221
Last Changed Date: 2013-01-28 22:31:07 +0100 (Mon, 28 Jan 2013)
В этом примере представлены следующие классы:
- lib.h/lib.cu
- kernel.h/kernel.cu
kernel.cu
содержит простое ядро CUDA и класс с открытым методом для вызова ядра CUDA. Класс lib содержит экземпляр ядра класса и метод, вызывающий открытый метод ядра класса.
После CMakeLists.txt
работает с этой конфигурацией:
cmake_minimum_required(VERSION 2.6.2)
project(Cuda-project)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake/cuda" ${CMAKE_MODULE_PATH})
find_package(CUDA QUIET REQUIRED)
set(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE OFF)
set(BUILD_SHARED_LIBS ON)
list(APPEND CUDA_NVCC_FLAGS -DBLAH="he he" -DTEST1="this is a test")
CUDA_ADD_LIBRARY(test_lib
kernel.cu
lib.cu
# SHARED
# STATIC
OPTIONS -DSTUFF="blah blah"
RELEASE --use_fast_math -DNDEBUG
DEBUG -g -DDEBUG
)
INSTALL(FILES lib.h kernel.h
DESTINATION include)
INSTALL(FILES "${CMAKE_BINARY_DIR}/libtest_lib.so"
DESTINATION lib)
Ответ 4
Я не мог заставить его работать с использованием CUDA_ADD_EXECUTABLE, поэтому я создал функцию, которая делает пользовательскую цель для этого.
function(add_cuda_exe_lib name files libraries is_lib)
set (obj_list)
foreach(file ${files})
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${file}.o
DEPENDS ${file}
COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --blue "Building NVCC Device object ${CMAKE_CURRENT_SOURCE_DIR}/${file}"
COMMAND ${CUDA_NVCC_EXECUTABLE} ${CUDA_NVCC_FLAGS} -dc "${CMAKE_CURRENT_SOURCE_DIR}/${file}" -o "${CMAKE_CURRENT_BINARY_DIR}/${file}.o"
COMMENT "Building ${CMAKE_CURRENT_SOURCE_DIR}/${file}"
VERBATIM
)
LIST(APPEND obj_list ${CMAKE_CURRENT_BINARY_DIR}/${file}.o)
endforeach()
set (lib_list)
LIST(APPEND lib_list "-lcudadevrt")
foreach(library_name ${libraries})
LIST(APPEND lib_list "-l${library_name}")
endforeach()
set (flags ${CUDA_NVCC_FLAGS})
if (is_lib)
LIST(APPEND flags "-dlink")
set (obj_name "${CMAKE_CURRENT_BINARY_DIR}/${name}.so")
else()
set (obj_name "${CMAKE_CURRENT_BINARY_DIR}/${name}")
endif()
add_custom_target(${name} ALL
COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red "Linking CXX executable ${name}"
COMMAND ${CUDA_NVCC_EXECUTABLE} ${flags} ${obj_list} ${lib_list} -o ${obj_name}
DEPENDS ${obj_list}
COMMENT "Linking ${name}"
)
endfunction()
function(add_cuda_exe name files libraries)
add_cuda_exe_lib(${name} "${files}" "${libraries}" OFF)
endfunction()
function(add_cuda_lib name files libraries)
add_cuda_exe_lib(${name} "${files}" "${libraries}" ON)
endfunction()
Теперь, чтобы сгенерировать lib, просто используйте:
add_cuda_lib(testar "devicemanager.cu;blub.cu" "")
Или для создания исполняемого файла:
add_cuda_exe(testar "devicemanager.cu;blub.cu" "")
Последний параметр - это список подключаемых библиотек.
Надеюсь, это поможет.