NVRTC
NVRTC是一个CUDA C++的运行时编译库。它接受以字符串形式表示的CUDA C++源代码,并创建可用于获取PTX的句柄。
由NVRTC生成的PTX字符串可以通过cuModuleLoadData和cuModuleLoadDataEx来加载,并通过CUDA驱动API的cuLinkAddData与其他模块链接。
NVRTC提供了一种在运行时编译和链接CUDA C++代码的方法,这可以带来更好的优化和性能。它是CUDA工具链的一部分,补充了离线编译的功能。
在没有NVRTC(或CUDA中没有运行时编译支持)的情况下,如果用户希望在应用程序或库中实现运行时编译,他们需要在运行时生成一个单独的进程来执行 nvcc。但是这种方法存在以下缺点:
- 编译开销往往比必要的更高。
- 因为在linux里设定NVCC编译环境比较麻烦,有坑,而通过上面的对于NVRTC的叙述,我们发现整个编译过程如果利用NVRTC的话会很简单,坑少。
- 不用我们在编译器里设定NVCC编译环境了。
NVRTC通过提供一个库接口来解决这些问题,消除了生成单独进程、磁盘I/O等相关的开销,同时保持应用程序部署的简单性。
- 
功能: - NVRTC提供了在运行时编译和链接CUDA C++代码的功能。
- 允许开发者动态地编译和使用CUDA内核,而不是在编译时就将它们固定下来。
- 这种灵活性可以带来更好的优化和性能。
 
- 
工作流程: - 开发者将CUDA C++代码以字符串的形式传递给NVRTC。
- NVRTC会编译这些代码,并生成可以被CUDA驱动API使用的PTX(并行线程执行)代码。
- 生成的PTX代码可以通过CUDA驱动API进行加载和链接,从而在运行时执行。
 
- 
应用场景: - 动态生成和执行CUDA内核,以适应不同的输入数据和环境。
- 在不重新编译整个应用程序的情况下,进行CUDA内核的更新和优化。
- 在机器学习等领域,根据模型的变化动态编译内核代码。
 
- 
优势: - 提高了代码的灵活性和可扩展性。
- 可以在运行时针对特定情况进行更好的优化和性能调整。
- 减少了静态编译所需的时间和资源。
 
NVRTC为CUDA开发者提供了一种强大的运行时编译工具,可以显著提升应用程序的灵活性和性能。
NVRTC(NVIDIA Runtime Compilation)是CUDA工具包发布的一部分,这些组件在CUDA工具包安装目录中的组织结构如下:
            
            
              cpp
              
              
            
          
          On Windows:
include\nvrtc.h
bin\nvrtc64_Major Release VersionMinor Release Version_0.dll
bin\nvrtc-builtins64_Major Release VersionMinor Release Version.dll
lib\x64\nvrtc.lib
lib\x64\nvrtc_static.lib
lib\x64\nvrtc-builtins_static.lib
doc\pdf\NVRTC_User_Guide.pdf
On Linux:
include/nvrtc.h
lib64/libnvrtc.so
lib64/libnvrtc.so.Major Release Version.Minor Release Version
lib64/libnvrtc.so.Major Release Version.Minor Release Version.<build version>
lib64/libnvrtc-builtins.so
lib64/libnvrtc-builtins.so.Major Release Version.Minor Release Version
lib64/libnvrtc-builtins.so.Major Release Version.Minor Release Version.<build version>
lib64/libnvrtc_static.a
lib64/libnvrtc-builtins_static.a
doc/pdf/NVRTC_User_Guide.pdf操作系统要求:
- Linux x86_64
- Linux ppc64le
- Linux aarch64
- Windows x86_64
GPU要求:
- SM>=2.0
其他要求:
- 安装CUDA Toolkit
- 安装CUDA driver
1.jpg 2.jpg
CUDA Sample里使用了NVRTC的sample有这些:
3.jpg
这里以simpleAssert_nvrtc为例进行代码讲解。
            
            
              cpp
              
              
            
          
          #include <stdio.h>
#include <cassert>
// Includes CUDA
#include <cuda_runtime.h>
#include "nvrtc_helper.h"
// Utilities and timing functions
#include <helper_functions.h>  // includes cuda.h and cuda_runtime_api.h
const char *sampleName = "simpleAssert_nvrtc";
// Auto-Verification Code
bool testResult = true;
// Declaration, forward
void runTest(int argc, char **argv);
// Program main
int main(int argc, char **argv) {
  printf("%s starting...\n", sampleName);
  runTest(argc, argv);
  exit(testResult ? EXIT_SUCCESS : EXIT_FAILURE);
}
void runTest(int argc, char **argv) {
  int Nblocks = 2;
  int Nthreads = 32;
  // Kernel configuration, where a one-dimensional
  // grid and one-dimensional blocks are configured.
  dim3 dimGrid(Nblocks);
  dim3 dimBlock(Nthreads);
  printf("Launch kernel to generate assertion failures\n");
  char *cubin, *kernel_file;
  size_t cubinSize;
  kernel_file = sdkFindFilePath("simpleAssert_kernel.cu", argv[0]);
  compileFileToCUBIN(kernel_file, argc, argv, &cubin, &cubinSize, 0);
  CUmodule module = loadCUBIN(cubin, argc, argv);
  CUfunction kernel_addr;
  checkCudaErrors(cuModuleGetFunction(&kernel_addr, module, "testKernel"));
  int count = 60;
  void *args[] = {(void *)&count};
  checkCudaErrors(cuLaunchKernel(
      kernel_addr, dimGrid.x, dimGrid.y, dimGrid.z, /* grid dim */
      dimBlock.x, dimBlock.y, dimBlock.z,           /* block dim */
      0, 0,                                         /* shared mem, stream */
      &args[0],                                     /* arguments */
      0));
  // Synchronize (flushes assert output).
  printf("\n-- Begin assert output\n\n");
  CUresult res = cuCtxSynchronize();
  printf("\n-- End assert output\n\n");
  // Check for errors and failed asserts in asynchronous kernel launch.
  if (res == CUDA_ERROR_ASSERT) {
    printf("Device assert failed as expected\n");
  }
  testResult = res == CUDA_ERROR_ASSERT;
}具体代码解释:
上面这段代码的主要功能和流程:
- 
包含必要的头文件,包括CUDA相关的头文件、辅助函数和计时工具。 
- 
定义了一个样本名称常量 sampleName。
- 
定义了一个 testResult布尔变量,用于记录测试结果。
- 
声明了一个 runTest函数,用于执行测试。
- 
main函数中主要内容:- 打印样本名称,表示程序开始运行。
- 调用 runTest函数执行测试。
- 根据 testResult的值,退出程序,返回success或failure状态。
 
- 
runTest函数的实现:- 设置 kernel 的网格和块的尺寸。
- 查找并编译 CUDA kernel 文件到 CUBIN 格式。
- 加载 CUBIN 文件,并获取 kernel 函数的地址。
- 设置 kernel 的参数,并启动 kernel 执行。
- 同步 CUDA 操作,并打印 assert 输出。
- 检查 kernel 执行过程中是否有 assert 失败,并更新 testResult变量。
 
这个程序演示了如何使用 NVRTC 在运行时编译和执行一个 CUDA kernel,该 kernel 会产生一些 assert 失败。它展示了如何检查 CUDA kernel 中的 assert 输出,并验证测试结果。
运行结果:
程序中N的默认值是60,所以有4个test fail
            
            
              cpp
              
              
            
          
          simpleAssert_nvrtc starting...
Launch kernel to generate assertion failures
sdkFindFilePath <simpleAssert_kernel.cu> in ./
> Using CUDA Device [0]: NVIDIA GeForce RTX 4080
> Using CUDA Device [0]: NVIDIA GeForce RTX 4080
> GPU Device has SM 8.9 compute capability
-- Begin assert output
./simpleAssert_kernel.cu:37: void testKernel(int): block: [1,0,0], thread: [28,0,0] Assertion `gtid < N` failed.
./simpleAssert_kernel.cu:37: void testKernel(int): block: [1,0,0], thread: [29,0,0] Assertion `gtid < N` failed.
./simpleAssert_kernel.cu:37: void testKernel(int): block: [1,0,0], thread: [30,0,0] Assertion `gtid < N` failed.
./simpleAssert_kernel.cu:37: void testKernel(int): block: [1,0,0], thread: [31,0,0] Assertion `gtid < N` failed.
-- End assert output
Device assert failed as expected