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

包括CUDA NVTC代码中的C标准头

  •  3
  • tmlen  · 技术社区  · 8 年前

    我正在编写一个CUDA内核,该内核在运行时使用nvrtc(CUDA版本9.2和nvrtc版本7.5)编译,它需要 stdint.h 头,以便 int32_t 等类型。

    如果我不使用include编写内核源代码,它将正常工作。例如,内核

    extern "C" __global__ void f() { ... }
    

    编译为ptx代码,其中f定义为 .visible .entry f .

    但是如果内核源代码是

    #include <stdint.h>
    extern "C" __global__ void f() { ... }
    

    IT报告 A function without execution space annotations (__host__/__device__/__global__) is considered a host function, and host functions are not allowed in JIT mode. (也没有 extern "C" )

    经过 -default-device 生成PTX代码 .visible .func f ,因此无法从主机调用函数。

    是否有方法在源代码中包含标题,并且仍然有 __global__ 进入功能?或者,一种方法来了解NVTC编译器在上使用的整数大小约定,以便 国际贸易组织 等等。类型可以手动定义?

    编辑: 显示问题的示例程序:

    #include <cstdlib>
    #include <string>
    #include <vector>
    #include <memory>
    #include <cassert>
    #include <iostream>
    
    #include <cuda.h>
    #include <cuda_runtime.h>
    #include <nvrtc.h>
    
    [[noreturn]] void fail(const std::string& msg, int code) {
        std::cerr << "error: " << msg << " (" << code << ')' << std::endl;
        std::exit(EXIT_FAILURE);
    }
    
    
    std::unique_ptr<char[]> compile_to_ptx(const char* program_source) {
        nvrtcResult rv;
    
        // create nvrtc program
        nvrtcProgram prog;
        rv = nvrtcCreateProgram(
            &prog,
            program_source,
            "program.cu",
            0,
            nullptr,
            nullptr
        );
        if(rv != NVRTC_SUCCESS) fail("nvrtcCreateProgram", rv);
    
        // compile nvrtc program
        std::vector<const char*> options = {
            "--gpu-architecture=compute_30"
        };
        //options.push_back("-default-device");
        rv = nvrtcCompileProgram(prog, options.size(), options.data());
        if(rv != NVRTC_SUCCESS) {
            std::size_t log_size;
            rv = nvrtcGetProgramLogSize(prog, &log_size);
            if(rv != NVRTC_SUCCESS) fail("nvrtcGetProgramLogSize", rv);
    
            auto log = std::make_unique<char[]>(log_size);
            rv = nvrtcGetProgramLog(prog, log.get());
            if(rv != NVRTC_SUCCESS) fail("nvrtcGetProgramLog", rv);
            assert(log[log_size - 1] == '\0');
    
            std::cerr << "Compile error; log:\n" << log.get() << std::endl;
    
            fail("nvrtcCompileProgram", rv);
        }
    
        // get ptx code
        std::size_t ptx_size;
        rv = nvrtcGetPTXSize(prog, &ptx_size);
        if(rv != NVRTC_SUCCESS) fail("nvrtcGetPTXSize", rv);
    
        auto ptx = std::make_unique<char[]>(ptx_size);
        rv = nvrtcGetPTX(prog, ptx.get());
        if(rv != NVRTC_SUCCESS) fail("nvrtcGetPTX", rv);
        assert(ptx[ptx_size - 1] == '\0');
    
        nvrtcDestroyProgram(&prog);
    
        return ptx;
    }
    
    const char program_source[] = R"%%%(
    //#include <stdint.h>
    extern "C" __global__ void f(int* in, int* out) {
        out[threadIdx.x] = in[threadIdx.x];
    }
    )%%%";
    
    int main() {
        CUresult rv;
    
        // initialize CUDA
        rv = cuInit(0);
        if(rv != CUDA_SUCCESS) fail("cuInit", rv);
    
        // compile program to ptx
        auto ptx = compile_to_ptx(program_source);
        std::cout << "PTX code:\n" << ptx.get() << std::endl;
    }
    

    什么时候? //#include <stdint.h> 在内核源代码中,它不再编译。什么时候? //options.push_back("-default-device"); 未注释它编译但不标记函数 f 作为 .entry .

    cmakelists.txt编译它(需要CUDA驱动程序api+nvrtc)

    cmake_minimum_required(VERSION 3.4)
    project(cudabug CXX)
    
    find_package(CUDA REQUIRED)
    
    set(CMAKE_CXX_STANDARD 14)
    set(CMAKE_CXX_STANDARD_REQUIRED 14)
    
    add_executable(cudabug cudabug.cc)
    include_directories(SYSTEM ${CUDA_INCLUDE_DIRS})
    link_directories(${CUDA_LIBRARY_DIRS})
    target_link_libraries(cudabug PUBLIC ${CUDA_LIBRARIES} nvrtc cuda)
    
    1 回复  |  直到 8 年前
        1
  •  0
  •   talonmies    7 年前

    [前言:这是一个非常简单的答案,并且是针对GNU工具链的(尽管我怀疑问题中的问题也是针对GNU工具链的)。

    这里的问题似乎与GNU标准头有关 features.h 被拉入 stdint.h 最后定义了很多具有默认值的存根函数 __host__ 编译空间和导致nvrtc爆炸。似乎 -default-device 选项将导致解决的glibc编译器功能集,从而使整个nvrtc编译器失败。

    您可以通过为排除所有主机功能的标准库预先定义一个特性集(非常简单)来击败这一点。将JIT内核代码更改为

    const char program_source[] = R"%%%(
    #define __ASSEMBLER__
    #define __extension__
    #include <stdint.h>
    extern "C" __global__ void f(int32_t* in, int32_t* out) {
        out[threadIdx.x] = in[threadIdx.x];
    }
    )%%%";
    

    给我这个:

    $ nvcc -std=c++14 -ccbin=g++-7 jit_header.cu -o jitheader -lnvrtc -lcuda
    $ ./jitheader 
    PTX code:
    //
    // Generated by NVIDIA NVVM Compiler
    //
    // Compiler Build ID: CL-24330188
    // Cuda compilation tools, release 9.2, V9.2.148
    // Based on LLVM 3.4svn
    //
    
    .version 6.2
    .target sm_30
    .address_size 64
    
        // .globl   f
    
    .visible .entry f(
        .param .u64 f_param_0,
        .param .u64 f_param_1
    )
    {
        .reg .b32   %r<3>;
        .reg .b64   %rd<8>;
    
    
        ld.param.u64    %rd1, [f_param_0];
        ld.param.u64    %rd2, [f_param_1];
        cvta.to.global.u64  %rd3, %rd2;
        cvta.to.global.u64  %rd4, %rd1;
        mov.u32     %r1, %tid.x;
        mul.wide.u32    %rd5, %r1, 4;
        add.s64     %rd6, %rd4, %rd5;
        ld.global.u32   %r2, [%rd6];
        add.s64     %rd7, %rd3, %rd5;
        st.global.u32   [%rd7], %r2;
        ret;
    }
    

    大警告:这对我试过的glibc系统有效。它可能无法与其他工具链或libc实现一起工作(如果它们确实有这个问题的话)。