如何使用CMake链接CUDA动态库?

pwuypxnk  于 2023-01-20  发布在  其他
关注(0)|答案(1)|浏览(284)

我想知道如何使用CMake动态链接CUDA库,我知道这似乎需要一些额外的限制,但不知 prop 体如何操作。下面是我写的一个简单的例子来说明我的问题。

目录结构:

Dir/
├── CMakeLists.txt
├── header.cuh
├── kernel.cu
└── main.cpp

环境:

  • 操作系统:Windows 11
  • GPU:RTX 3060笔记本电脑
  • CUDA工具包:11.6
  • 平台:Visual Studio 2022
    标题.cuh:
#include "stdio.h"
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

extern "C" void f();

内核.cu:

#include "header.cuh"

void __global__ print()
{
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    printf("%d\n", idx);
}

void f()
{
    print<<<1, 10>>>();
}

主要cpp:

#include "header.cuh"

extern "C" void f();

int main()
{
    f();
    return 0;
}

CMakeLists.文本文件:

cmake_minimum_required(VERSION 3.17)
project(test)
set(CMAKE_CXX_STANDARD 17)

find_package(CUDA REQUIRED)
enable_language("CUDA")
set(CMAKE_CUDA_STANDARD 14)
set(CUDA_SEPARABLE_COMPILATION ON)

string(APPEND CMAKE_CUDA_FLAGS " -rdc=true --cudart shared")

add_library(CUDA_COMP SHARED header.cuh kernel.cu)
set_property(TARGET CUDA_COMP PROPERTY CUDA_ARCHITECTURES 86-real 86-virtual)

add_executable(main main.cpp)

target_link_libraries(main CUDA_COMP)

项目可以配置成功,但是在构建时出现了函数main中引用的外部符号f无法解析的问题。
我也在Stackoverflow上查找了相应的解决方案,但是没有成功,例如,Stackoverflow上的一个答案提到在cmake中添加**”-rdc=true --cudart shared”,我也做了同样的操作(参见CMakeLists.txt的第10行**)。
这个问题困扰我很久了,希望您能告诉我问题的原因和解决方法,非常感谢!

5gfr0r5j

5gfr0r5j1#

OP的代码有很多问题。

CUDA/C++问题:
  • CUDA现在是一种C++方言,而不是C。因此,我不会将f()声明为extern "C"
  • 头文件似乎没有理由包含任何东西。当实现需要某些东西,而接口不需要时,include不应该是接口的一部分。
  • 如果你想使用这个头文件来连接非CUDA C++代码(一个.cpp文件),它应该被命名为.h(或.hpp)。我希望.cuh文件只包含在.cu文件中。人们通常也会在接口和实现上使用相同的名称,所以我将头文件重命名为kernel.h
  • 顶盖护罩丢失。
  • 为什么要在main.cpp中重新声明f()?这就是头文件的作用。
  • 您忘记了在内核启动后进行同步,因此应用程序很可能根本不打印任何内容。
C制造问题:
  • 不鼓励在CMakeLists.txt中全局设置属性等。应该尽可能使用特定于目标的API。这些全局属性中的一些必须在project()之前设置才能工作。
  • CMake在设置CUDA_SEPARABLE_COMPILATION时自动设置-rdc=true
  • CUDA可分离编译仅在内核使用在不同翻译单元中定义的设备函数时才需要。我不确定OP是否认为他需要它,或者他需要它的部分在创建最小示例时被删除了。
  • CUDA_RUNTIME_LIBRARY属性用于动态链接CUDA运行时。
  • 不赞成使用find_package(CUDA),而赞成使用CUDA作为语言。在极少数情况下,一个人不想使用CUDA,但仍然需要找到CUDA工具包,还有FindCUDAToolkit。OP只需要语言。但是链接的文档也列出了工具包附带的所有CUDA库。在这里我们可以看到,大多数库都提供了_static版本来区分静态链接和动态链接。当使用CUDA时,也可以使用CUDA::版本,但是没有CUDA::“命名空间”。当使用语言时,不要使用target_link_libraries(... cudart)**,因为这不会影响CUDA_RUNTIME_LIBRARY属性,也就是说,你可能会得到某种未定义的CMake行为。但是,当你想动态链接时,例如CUBLAS,你可以使用target_link_libraries(... cublas)
  • 只要项目不使用某些特定于架构的内部函数,就不应将CUDA架构硬编码到CMakeLists.txt中。相反,应在配置时指定架构。CMake 3.18在版本中添加了CMAKE_CUDA_ARCHITECTURES,因此我建议将最低配置设置为3.18而不是3.17。然后,您可以调用cmake -DCMAKE_CUDA_ARCHITECTURES=86(不确定这在VisualStudio中是如何处理的)。我为X1 M28 N1 X添加了一个默认值,在需要时仍然可以在配置时覆盖它。

固定示例

以下文件适用于Linux下的CMake 3.23.1、CUDA 11.8.0和GCC 11.3.0:
kernel.h

#pragma once

void f();

kernel.cu

#include "kernel.h"

#include <cstdio>

void __global__ print()
{
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    std::printf("%d\n", idx);
}

void f()
{
    print<<<1, 10>>>();
    cudaDeviceSynchronize();
}

main.cpp

#include "kernel.h"

int main()
{
    f();
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.18)

# this has to be set before project()
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
  set(CMAKE_CUDA_ARCHITECTURES 86)
endif()

project(test LANGUAGES CXX CUDA)

add_library(cuda_comp SHARED kernel.h kernel.cu)

# this command would only set the minimum standard,
# i.e. CMake can still decide to use -std=c++17 instead
# if the given compilers support C++17
target_compile_features(cuda_comp PRIVATE cuda_std_14)

set_target_properties(cuda_comp
        PROPERTIES
                CUDA_RUNTIME_LIBRARY Shared
                # CUDA_STANDARD 14 # this one cannot be changed by CMake
                # CUDA_SEPARABLE_COMPILATION ON # not needed for this example
)

add_executable(main main.cpp)

target_compile_features(main PRIVATE cxx_std_17)
# set_target_properties(main CXX_STANDARD 17)

target_link_libraries(main cuda_comp)

相关问题