从cmake测试cuda功能GPU存在的最简单方法?

Chr*_*uns 14 build-automation cuda gpu cmake

我们有一些夜间构建的机器安装了cuda库,但没有安装cuda的GPU.这些机器能够构建启用cuda的程序,但它们无法运行这些程序.

在我们的自动夜间构建过程中,我们的cmake脚本使用cmake命令

find_package(CUDA)

确定是否安装了cuda软件.这会CUDA_FOUND在安装了cuda软件的平台上设置cmake变量.这很棒,而且效果很好.何时CUDA_FOUND设置,可以构建启用cuda的程序.即使机器没有cuda功能的GPU.

但是,使用cuda的测试程序自然会在非GPU cuda机器上失败,导致我们的夜间仪表板看起来"脏".所以我希望cmake避免在这样的机器上运行这些测试.但我仍然想在这些机器上构建cuda软件.

得到一个积极的CUDA_FOUND结果后,我想测试一个实际GPU的存在,然后设置一个变量,比如说CUDA_GPU_FOUND,以反映这一点.

让cmake测试是否存在具有cuda功能的gpu的最简单方法是什么?

这需要在三个平台上运行:Windows与MSVC,Mac和Linux.(这就是为什么我们首先使用cmake)

编辑:在答案中有一些很好看的建议,如何编写程序来测试GPU的存在.仍然缺少的是让CMake在配置时编译和运行该程序的方法.我怀疑TRY_RUNCMake 中的命令在这里很重要,但不幸的是,这个命令几乎没有记录,我无法弄清楚如何让它工作.这个问题的CMake部分可能是一个更加困难的问题.也许我应该把这个问题作为两个单独的问题......

Chr*_*uns 18

这个问题的答案包括两部分:

  1. 用于检测具有cuda功能的GPU的存在的程序.
  2. CMake代码,用于在配置时编译,运行和解释该程序的结果.

对于第1部分,gpu嗅探程序,我从fabrizioM提供的答案开始,因为它非常紧凑.我很快发现,我需要在未知的答案中找到许多细节,以使其运作良好.我最终得到的是以下C源文件,我将其命名为has_cuda_gpu.c:

#include <stdio.h>
#include <cuda_runtime.h>

int main() {
    int deviceCount, device;
    int gpuDeviceCount = 0;
    struct cudaDeviceProp properties;
    cudaError_t cudaResultCode = cudaGetDeviceCount(&deviceCount);
    if (cudaResultCode != cudaSuccess) 
        deviceCount = 0;
    /* machines with no GPUs can still report one emulation device */
    for (device = 0; device < deviceCount; ++device) {
        cudaGetDeviceProperties(&properties, device);
        if (properties.major != 9999) /* 9999 means emulation only */
            ++gpuDeviceCount;
    }
    printf("%d GPU CUDA device(s) found\n", gpuDeviceCount);

    /* don't just return the number of gpus, because other runtime cuda
       errors can also yield non-zero return values */
    if (gpuDeviceCount > 0)
        return 0; /* success */
    else
        return 1; /* failure */
}
Run Code Online (Sandbox Code Playgroud)

请注意,在找到启用cuda的GPU的情况下,返回码为零.这是因为在我的一台us-cuda-but-no-GPU机器上,该程序会产生一个非零退出代码的运行时错误.所以任何非零退出代码都被解释为"cuda在这台机器上不起作用".

您可能会问我为什么不在非GPU机器上使用cuda仿真模式.这是因为仿真模式是错误的.我只想调试我的代码,并解决cuda GPU代码中的错误.我没时间调试模拟器.

问题的第二部分是使用此测试程序的cmake代码.经过一番努力,我已经弄明白了.以下块是较大CMakeLists.txt文件的一部分:

find_package(CUDA)
if(CUDA_FOUND)
    try_run(RUN_RESULT_VAR COMPILE_RESULT_VAR
        ${CMAKE_BINARY_DIR} 
        ${CMAKE_CURRENT_SOURCE_DIR}/has_cuda_gpu.c
        CMAKE_FLAGS 
            -DINCLUDE_DIRECTORIES:STRING=${CUDA_TOOLKIT_INCLUDE}
            -DLINK_LIBRARIES:STRING=${CUDA_CUDART_LIBRARY}
        COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT_VAR
        RUN_OUTPUT_VARIABLE RUN_OUTPUT_VAR)
    message("${RUN_OUTPUT_VAR}") # Display number of GPUs found
    # COMPILE_RESULT_VAR is TRUE when compile succeeds
    # RUN_RESULT_VAR is zero when a GPU is found
    if(COMPILE_RESULT_VAR AND NOT RUN_RESULT_VAR)
        set(CUDA_HAVE_GPU TRUE CACHE BOOL "Whether CUDA-capable GPU is present")
    else()
        set(CUDA_HAVE_GPU FALSE CACHE BOOL "Whether CUDA-capable GPU is present")
    endif()
endif(CUDA_FOUND)
Run Code Online (Sandbox Code Playgroud)

CUDA_HAVE_GPU在cmake中设置了一个布尔变量,随后可用于触发条件操作.

我花了很长时间才弄清楚include和link参数需要放在CMAKE_FLAGS节中,以及语法应该是什么.该try_run文档很轻,但中更多信息try_compile文档,这是密切相关的命令.在让这个工作之前,我仍然需要在网上搜索try_compile和try_run的示例.

另一个棘手但重要的细节是第三个参数try_run,即"bindir".您可能应该始终将其设置为${CMAKE_BINARY_DIR}.特别是,${CMAKE_CURRENT_BINARY_DIR}如果您位于项目的子目录中,请不要将其设置为.CMake希望CMakeFiles/CMakeTmp在bindir中找到子目录,并在该目录不存在时发出错误.只是使用${CMAKE_BINARY_DIR},这是这些子目录似乎自然存在的一个位置.


fab*_*ioM 9

写一个简单的程序就好

#include<cuda.h>

int main (){
    int deviceCount;
    cudaError_t e = cudaGetDeviceCount(&deviceCount);
    return e == cudaSuccess ? deviceCount : -1;
}
Run Code Online (Sandbox Code Playgroud)

并检查返回值.