Dynamic Parallelism 机制实现两个 cuda kernel 动态并行的示例

[复制链接]
发表于 2025-12-14 13:21:01 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
1,简介 Dynamic Parallelism

Dynamic Parallelism 是 CUDA 的一项特性,允许一个 CUDA kernel 在 GPU 上直接启动其他 CUDA kernel,而无需返回主机。这种特性使得在 GPU 上可以实现更复杂的并行盘算模式,特别实用于递归算法、分条理盘算和自顺应网格细化等场景。

2,示例一:kernel 启动单个 kernel
  1. #include <iostream>
  2. #include <cuda_runtime.h>
  3. // 子 kernel,计算每个元素的平方
  4. __global__ void childKernel(int* data, int n) {
  5.     int idx = blockIdx.x * blockDim.x + threadIdx.x;
  6.     if (idx < n) {
  7.         data[idx] +=100;
  8.     }
  9. }
  10. // 父 kernel,启动子 kernel
  11. __global__ void parentKernel(int* data, int n) {
  12.     // 假设每个 block 处理 256 个元素
  13.     int threadsPerBlock = 1024;
  14.     int blocks = (n + threadsPerBlock - 1) / threadsPerBlock;
  15.     // 启动子 kernel
  16.     childKernel<<<blocks, threadsPerBlock>>>(data, n);
  17.     // 等待子 kernel 完成
  18. //    cudaDeviceSynchronize();
  19. }
  20. // 主机代码
  21. int main() {
  22.     const int n = 1024*1024*512;
  23.     int* h_data = new int[n];
  24.     int* d_data;
  25.     // 初始化数据
  26.     for (int i = 0; i < n; ++i) {
  27.         h_data[i] = i;
  28.     }
  29.     // 分配设备内存
  30.     cudaMalloc(&d_data, n * sizeof(int));
  31.     cudaMemcpy(d_data, h_data, n * sizeof(int), cudaMemcpyHostToDevice);
  32.     // 启动父 kernel
  33.     parentKernel<<<1, 1>>>(d_data, n);
  34.     // 复制结果回主机
  35.     cudaMemcpy(h_data, d_data, n * sizeof(int), cudaMemcpyDeviceToHost);
  36.     // 输出结果
  37.     for (int i = 0; i < 10; ++i) {
  38.         std::cout << h_data[i] << " ";
  39.     }
  40.     std::cout << std::endl;
  41.     // 清理
  42.     delete[] h_data;
  43.     cudaFree(d_data);
  44.     return 0;
  45. }
复制代码
编译:
 nvcc hello.cu -rdc=true -arch=sm_75 -lcudart -Xcompiler -lcudadevrt
大概Makefile:
  1. DEV_FLAGS :=  -rdc=true -arch=sm_75 -Xcompiler
  2. EXE := hello
  3. aLL: $(EXE)
  4. %: %.cu
  5.         nvcc $< -o $@ $(DEV_FLAGS) -lcudart -lcudadevrt
  6. .PHONY: clean
  7. clean:
  8.         -rm -rf $(EXE)
复制代码
运行:

 
3,示例二:kernel 启动两个 kernel
  1. #include <iostream>
  2. #include <cuda_runtime.h>
  3. // data[2k] += 100;
  4. __global__ void childKernel_A(int* data, int n) {
  5.     int idx = blockIdx.x * blockDim.x + threadIdx.x;
  6.     if (idx < n/2) {
  7.         data[2*idx] += 100;
  8.     }
  9. }
  10. //data[2k+]  += 200;
  11. __global__ void childKernel_B(int* data, int n) {
  12.     int idx = blockIdx.x * blockDim.x + threadIdx.x;
  13.     if (idx < n/2) {
  14.         data[2*idx+1] += 200;
  15.     }
  16. }
  17. // launch kernels
  18. __global__ void parentKernel(int* data, int n) {
  19.         if(threadIdx.x==0){
  20.                 int threadsPerBlock = 1024;
  21.                 int blocks = (n/2 + threadsPerBlock - 1) / threadsPerBlock;
  22.                 childKernel_A<<<blocks, threadsPerBlock>>>(data, n);
  23.                  //cudaDeviceSynchronize();
  24.         }
  25.         else if(threadIdx.x==1){
  26.                
  27.                 int threadsPerBlock = 1024;
  28.                 int blocks = (n/2 + threadsPerBlock - 1) / threadsPerBlock;
  29.                 childKernel_B<<<blocks, threadsPerBlock>>>(data, n);
  30.         }
  31. }
  32. int main() {
  33.     const int n = 1024*1024*512;
  34.     int* h_data = new int[n];
  35.     int* d_data;
  36.     for (int i = 0; i < n; ++i) {
  37.         h_data[i] = i;
  38.     }
  39.     cudaMalloc(&d_data, n * sizeof(int));
  40.     cudaMemcpy(d_data, h_data, n * sizeof(int), cudaMemcpyHostToDevice);
  41.     parentKernel<<<1, 2>>>(d_data, n);
  42.     cudaMemcpy(h_data, d_data, n * sizeof(int), cudaMemcpyDeviceToHost);
  43.    
  44.     for (int i = 0; i < 10; ++i) {
  45.         std::cout << h_data[i] << " ";
  46.     }
  47.     std::cout << std::endl;
  48.     delete[] h_data;
  49.     cudaFree(d_data);
  50.     return 0;
  51. }
复制代码
 nvcc hello.cu -rdc=true -arch=sm_75 -lcudart -Xcompiler -lcudadevrt
运行:


4,总结:


4.1 上风


镌汰主机干预: 通过在装备上直接启动子 kernel,镌汰了主机与装备之间的通讯开销。
简化代码结构: 允许在 GPU 上实现更复杂的控制流和盘算模式。
进步性能: 在某些场景下,通过镌汰主机干预和优化并行盘算,可以进步团体性能。
4.2 利用场景

须要与其他 GPU 盘算框架集成,须要风雅控制 GPU 资源和举动。
递归算法: 如快速排序、树形数据结构遍历。
自顺应网格细化: 在盘算流体力学和其他科学盘算中。
分条理盘算: 如多级网格方法。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表