代码之家  ›  专栏  ›  技术社区  ›  einpoklum

在CMake中确定nvcc需要哪些gencode(compute_,arch_)值

  •  8
  • einpoklum  · 技术社区  · 9 年前

    我使用CMake作为我的代码的构建系统,这涉及到CUDA。我在考虑自动完成决定哪个 compute_XX arch_XX 我需要传递给我的nvcc,以便为我当前机器上的GPU进行编译。

    • 有没有办法做到这一点:

      1. 使用NVIDIA GPU部署套件?
      2. 没有NVIDIA GPU部署套件?
    • CMake的 FindCUDA 帮助您确定这些开关的值?

    3 回复  |  直到 8 年前
        1
  •  9
  •   user968363 user968363    8 年前

    我的策略是编译并运行一个bash脚本,该脚本探测卡并返回cmake的gencode。灵感来自 University of Chicago's SLURM 。要处理错误或多个gpu或其他情况,请根据需要进行修改。

    在项目文件夹中创建一个文件cudaComputeVersion。bash,并确保它可以从shell执行。在此文件中输入:

    #!/bin/bash
    
    # create a 'here document' that is code we compile and use to probe the card
    cat << EOF > /tmp/cudaComputeVersion.cu
    #include <stdio.h>
    int main()
    {
    cudaDeviceProp prop;
    cudaGetDeviceProperties(&prop,0);
    int v = prop.major * 10 + prop.minor;
    printf("-gencode arch=compute_%d,code=sm_%d\n",v,v);
    }
    EOF
    
    # probe the card and cleanup
    /usr/local/cuda/bin/nvcc /tmp/cudaComputeVersion.cu -o /tmp/cudaComputeVersion
    /tmp/cudaComputeVersion
    rm /tmp/cudaComputeVersion.cu
    rm /tmp/cudaComputeVersion
    

    在您的CMakeLists中。txt输入:

    # at cmake-build-time, probe the card and set a cmake variable
    execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cudaComputeVersion.bash OUTPUT_VARIABLE GENCODE)
    # at project-compile-time, include the gencode into the compile options
    set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS}; "${GENCODE}")
    
    # this makes CMake all chatty and allows you to see that GENCODE was set correctly
    set(CMAKE_VERBOSE_MAKEFILE TRUE)
    

    干杯

        2
  •  5
  •   Martin Valgur xenoid    5 年前

    您可以使用 cuda_select_nvcc_arch_flags() 中的宏 FindCUDA 使用CMake 3.7或更高版本时,无需任何附加脚本。

    include(FindCUDA)
    set(CUDA_ARCH_LIST Auto CACHE STRING
        "List of CUDA architectures (e.g. Pascal, Volta, etc) or \
    compute capability versions (6.1, 7.0, etc) to generate code for. \
    Set to Auto for automatic detection (default)."
    )
    cuda_select_nvcc_arch_flags(CUDA_ARCH_FLAGS ${CUDA_ARCH_LIST})
    list(APPEND CUDA_NVCC_FLAGS ${CUDA_ARCH_FLAGS})
    

    上述集合 CUDA_ARCH_FLAGS -gencode arch=compute_61,code=sm_61 例如,在我的机器上。 这个 CUDA_ARCH_LIST 缓存变量可以由用户配置为生成特定计算能力的代码,而不是自动检测。

    注意:自CMake 3.10以来,FindCUDA模块已被弃用 cuda_select_nvcc_arch_flags() 最新的CMake版本(v3.14)中似乎还提供了宏。看看这个 relevant issue 有关详细信息,请访问CMake问题跟踪程序。

        3
  •  4
  •   einpoklum    6 年前

    对@orthosteroid的答案有一点改进,这几乎可以确保生成一个唯一的临时文件,并且只需要一个而不是两个临时文件。

    下面是 scripts/get_cuda_sm.sh :

    #!/bin/bash 
    #
    # Prints the compute capability of the first CUDA device installed
    # on the system, or alternatively the device whose index is the
    # first command-line argument
    
    device_index=${1:-0}
    timestamp=$(date +%s.%N)
    gcc_binary=$(which g++)
    gcc_binary=${gcc_binary:-g++}
    cuda_root=${CUDA_DIR:-/usr/local/cuda}
    CUDA_INCLUDE_DIRS=${CUDA_INCLUDE_DIRS:-${cuda_root}/include}
    CUDA_CUDART_LIBRARY=${CUDA_CUDART_LIBRARY:-${cuda_root}/lib64/libcudart.so}
    generated_binary="/tmp/cuda-compute-version-helper-$$-$timestamp"
    # create a 'here document' that is code we compile and use to probe the card
    source_code="$(cat << EOF 
    #include <stdio.h>
    #include <cuda_runtime_api.h>
    
    int main()
    {
            cudaDeviceProp prop;
            cudaError_t status;
            int device_count;
            status = cudaGetDeviceCount(&device_count);
            if (status != cudaSuccess) { 
                    fprintf(stderr,"cudaGetDeviceCount() failed: %s\n", cudaGetErrorString(status)); 
                    return -1;
            }
            if (${device_index} >= device_count) {
                    fprintf(stderr, "Specified device index %d exceeds the maximum (the device count on this system is %d)\n", ${device_index}, device_count);
                    return -1;
            }
            status = cudaGetDeviceProperties(&prop, ${device_index});
            if (status != cudaSuccess) { 
                    fprintf(stderr,"cudaGetDeviceProperties() for device ${device_index} failed: %s\n", cudaGetErrorString(status)); 
                    return -1;
            }
            int v = prop.major * 10 + prop.minor;
            printf("%d\\n", v);
    }
    EOF
    )"
    echo "$source_code" | $gcc_binary -x c++ -I"$CUDA_INCLUDE_DIRS" -o "$generated_binary" - -x none "$CUDA_CUDART_LIBRARY"
    
    # probe the card and cleanup
    
    $generated_binary
    rm $generated_binary
    

    下面是 CMakeLists.txt 或CMake模块:

    if (NOT CUDA_TARGET_COMPUTE_CAPABILITY)
        if("$ENV{CUDA_SM}" STREQUAL "")
            set(ENV{CUDA_INCLUDE_DIRS} "${CUDA_INCLUDE_DIRS}")
            set(ENV{CUDA_CUDART_LIBRARY} "${CUDA_CUDART_LIBRARY}")
            set(ENV{CMAKE_CXX_COMPILER} "${CMAKE_CXX_COMPILER}")
            execute_process(COMMAND 
                bash -c "${CMAKE_CURRENT_SOURCE_DIR}/scripts/get_cuda_sm.sh" 
                OUTPUT_VARIABLE CUDA_TARGET_COMPUTE_CAPABILITY_)
        else()
            set(CUDA_TARGET_COMPUTE_CAPABILITY_ $ENV{CUDA_SM})
        endif()
    
        set(CUDA_TARGET_COMPUTE_CAPABILITY "${CUDA_TARGET_COMPUTE_CAPABILITY_}" 
            CACHE STRING "CUDA compute capability of the (first) CUDA device on \
            the system, in XY format (like the X.Y format but no dot); see table \
            of features and capabilities by capability X.Y value at \
            https://en.wikipedia.org/wiki/CUDA#Version_features_and_specifications")
    
        execute_process(COMMAND 
            bash -c "echo -n $(echo ${CUDA_TARGET_COMPUTE_CAPABILITY})" 
            OUTPUT_VARIABLE CUDA_TARGET_COMPUTE_CAPABILITY)
        execute_process(COMMAND 
            bash -c "echo ${CUDA_TARGET_COMPUTE_CAPABILITY} | sed 's/^\\([0-9]\\)\\([0-9]\\)/\\1.\\2/;' | xargs echo -n" 
            OUTPUT_VARIABLE FORMATTED_COMPUTE_CAPABILITY)
    
        message(STATUS 
            "CUDA device-side code will assume compute capability \
            ${FORMATTED_COMPUTE_CAPABILITY}")
    endif()
    
    set(CUDA_GENCODE
        "arch=compute_${CUDA_TARGET_COMPUTE_CAPABILITY}, code=compute_${CUDA_TARGET_COMPUTE_CAPABILITY}")
    set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -gencode ${CUDA_GENCODE} )