CUDA 5.0使用CMake对库进行分离编译

11
我的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_varcuda_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
#)

有人知道如何使用cmake编译和链接cuda库吗?谢谢。

编辑: 在朋友向FindCUDA.cmake的开发人员咨询后,发现了在FindCUDA.cmake提供的示例中存在的一个错误(https://gforge.sci.utah.edu/gf/project/findcuda/scmsvn/?action=browse&path=%2Fcheckout%2Ftrunk%2FFindCuda.html)。 现在我可以构建这个示例了。 在我的项目中,我可以像需要的那样构建库,方法是使用以下命令(需要cmake 2.8.10):

set(LIB_TYPE SHARED)
set(CUDA_SEPARABLE_COMPILATION ON)
cuda_add_library(gpuacceleration ${LIB_TYPE} 
 blub.cu
 blab.cu
)

BUT: 我无法链接到这个库。当我没有进行单独编译的情况下构建该库时,我能够链接到它。 现在出现以下错误:

 undefined reference to `__cudaRegisterLinkedBinary_53_tmpxft_00005ab4_00000000_6_blub_cpp1_ii_d07d5695'

对于在接口中使用的每个带有函数的文件都需要。这似乎很奇怪,因为它没有任何警告等就可以构建。

编辑:我最终弄清楚如何做到这一点。有关详细信息,请参见 @PHD 和我的答案。


这已经被确认为FindCUDA中的一个bug(请查看我下面的回答)。 - kalj
4个回答

5

我终于让它跑起来了 ;)

除了 @PHD 的回答和我的评论,我还修改了我的 CMakeLists.txt 文件中的 set(BUILD_SHARED_LIBS OFF),因为根据 nvcc 手动 v5.0 第40页的说明,独立编译不支持共享库。

除此之外,使用仓库的最新版本(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)

编辑:这个方法不起作用!问题在于当在主项目中构建可执行文件并链接生成的库时,所有cuda函数(如cudaMalloc)都存在未定义的引用。

正在继续研究中。


3
你最终找到了解决这个问题的方法吗? - BenC
没有解决方案,但是请查看下面关于这个问题的更多信息。 - kalj

3

编辑(2016-03-15):确认这是FindCUDA的一个错误:https://cmake.org/Bug/view.php?id=15157


简而言之:这似乎是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 $@ $^

%.o: %.cu
    $(NVCC) $(NVCCFLAGS) -dc -c -o $@ $^

$(LIB): module.o
    ar cr $@ $^

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不执行这个额外的链接步骤?

否则,我认为这是一个bug。你同意吗?我可以向CMake提交一个bug报告。


2

我使用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内核。类库包含一个类内核的实例和一个调用类内核的公共方法。
以下 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)

1
以下内容缺失:set(CUDA_SEPARATE_COMPILATION ON)。除此之外,我还无法链接生成的库。SVN中有一个新版本的findcuda,我很快会进行测试。 - soriak
1
我终于让它跑起来了 ;) 除了@PHD的答案和我的先前评论之外,根据nvcc手动v5.0第40页的说明,我设置了“BUILD_SHARED_LIBS OFF”,因为独立编译不支持共享库。 - soriak

1

我无法使用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()

现在,要生成一个库,只需使用以下命令:

add_cuda_lib(testar "devicemanager.cu;blub.cu" "")

或者使用这个来生成可执行文件:

add_cuda_exe(testar "devicemanager.cu;blub.cu" "")

最后一个参数是要附加的库列表。
希望有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接