当前位置: 首页 > article >正文

[CUDA] cuda kernel开发记录

文章目录

  • 1. kernel基本书写
  • 2. grid-block设置
  • 3. __device__ 使用
  • 4. launch_bounds
  • 5. kernel问题排查
  • 6. CUDA_KERNEL_LOOP的使用
    • 6.1 基本写法
    • 6.2 使用注意事项
  • 7. kernel中打印GPU数据

1. kernel基本书写

# 基本步骤
分配host内存,并进行数据初始化;
分配device内存,并从host将数据拷贝到device上;
调用CUDA的 kernel 函数在device上完成指定的运算;
将device上的运算结果拷贝到host上;
释放device和host上分配的内存。

2. grid-block设置

// 1维时,可以直接使用int来表示
const int block_size = 128;
const int grid_size = (size + block_size - 1 ) / block_size;

// 多维时,可以使用dim3数据类型
dim3 grid_dim1, block_dim1(32, 32);
grid_dim1.x = (kNTotal + 32 - 1) / 32;
grid_dim1.y = (kMTotal + 32 - 1) / 32;

3. device 使用

  • device一些struct用法
template<typename T>
struct NonZeroOp
{
    __host__ __device__ __forceinline__ bool operator()(const T& a) const {
      return (a!=T(0));
    }
};
  • device属性设置
extern __attribute__((device)) __attribute__((cudart_builtin)) cudaError_t cudaMalloc(void **devPtr, size_t size);
等同于
extern __host__ __device__ cudaError_t cudaMalloc(void **devPtr, size_t size);

4. launch_bounds

  • launch_bounds的使用

5. kernel问题排查

  • kernel中invalid argument错误,一般是kernel配置的参数问题,这个需要确定grid size, block size是否为0; 如果为零则会出invalid的错误。
  • 如果是一些stream爆出错误,则考虑是否stream上的kernel有问题,需要通过每个kernel调用后加入cudaGetLastError或者cudaPeekAtLastError() 来确定是哪个函数。
  • 所以以后写kernel函数,最好在调用函数后面加上cudaPeekAtLastError() 保证kernel出错能及时报出问题。
CUDA_CHECK(cudaPeekAtLastError()); // 不会清理错误flag状态。
CUDA_CHECK(cudaGetLastError()); // 会清理错误flag状态。

6. CUDA_KERNEL_LOOP的使用

6.1 基本写法

  • 一般写kernel函数时,最好多使用CUDA_KERNEL_LOOP

  • 注意__global__ void kernel中 的N不能是引用

// template <typename T> \
// __global__ void ##name_kernel(T* buf, const int N) { \
//     int tid = threadIdx.x + blockIdx.x * blockDim.x; \
//     buf[tid] = op(buf[tid]); \
// }
  • 注意基本写法index 通过循环来,这样保证一个block的thread读取的连续数据
// 利用这种宏来保证kernel数量小于处理数据的数量时,也能处理全数据。
#define CUDA_KERNEL_LOOP(i, n)                                 \
  for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \
       i += blockDim.x * gridDim.x)

6.2 使用注意事项

  • 但写loop cuda kernel的时候,需要注意最好kernel的个数按照数据赋值的地址的数量进行设置,这样防止地址访问冲突。
  • 但是如果kernel loop的过程中,存在两个或两个以上的kernel会访问一个地址,尤其累加或累乘操作,需要注意用cuda提供的原子操作,防止多个kernel对同一个地址同时写,从而导致结果不正确的问题。

template <typename scalar_t>
__global__ void devoxelize_forward_kernel2(int c, int N,
                                          const int *__restrict__ indices,
                                          const scalar_t *__restrict__ weight,
                                          const scalar_t *__restrict__ feat,
                                          scalar_t *__restrict__ out)
{
  // index is for indices or weights
  CUDA_KERNEL_LOOP(index, N*c) {
	  int i = index / N;
	  int j = index % N;
	
	  if (i < 8) {
	    const int indices_ = *(indices + index);
	    const scalar_t weight_ = *(weight + index);
	    const scalar_t *feat_ = feat + indices_ * c;
	
	    scalar_t cur_feat;
	    for(int k = 0; k < c; k++) {
	      cur_feat = 0;
	      if (indices_ >= 0) cur_feat = feat_[k];
	      // before: out[j * c + k] += weight_ * cur_feat;
	      // fix the bug, conflict.
	      atomicAdd(out + j * c + k, weight_ * cur_feat);  
	    }
	  }
  }
}

7. kernel中打印GPU数据

  • 当debug cuda kernel的时候 打印kernel中一些关键值的变化很重要,对排查问题很有帮助,但是cuda kernel只能用printf打印,注意打印float的时候,要小数点多一些,因为有效非零值会小数点后几位才有值。
  • 另外gpu上的数据只能用kernel进行封装printf的方式打印; 另一种方法就是将gpu数据copy到cpu后,再打印。
template <typename Type>
__global__ void PrintKernel(const Type* data, int start, int end) {
  for (int i = start; i < end; ++i) {
    if (std::is_floating_point<Type>::value) {
      printf("| %.7f ", static_cast<float>(data[i]));
    } else {
      printf("| %.1f ", static_cast<float>(data[i]));
    }
  }
  printf("\n");
}
template <typename Type>
void Print(const Type* data, int start, int end) {
  PrintGpuDataKernel<Type><<<1, 1, 0>>>(data, start, end);
}


http://www.kler.cn/a/390896.html

相关文章:

  • NUXT3学习日记一(在我git中拉取代码、文件讲解)
  • 【Spring】@Autowired与@Resource的区别
  • group_concat配置影响程序出bug
  • aws(学习笔记第十二课) 使用AWS的RDS-MySQL
  • 企业如何提高团队管理的能力?
  • 【JavaEE进阶】导读
  • HTTP TCP三次握手深入解析
  • ESLint 使用教程(七):ESLint还能校验JSON文件内容?
  • XSS漏洞--常用payload及绕过
  • 关于解决使用VMWare内的虚拟机无法识别USB问题小结
  • 【JavaEE】文件io
  • Yocto项目 - 小心Overrides机制还用在Tasks中
  • mysql占用内存过大问题排查
  • java 递归算法案例讲解
  • Linux——简单认识vim、gcc以及make/Makefile
  • Python数据分析NumPy和pandas(二十六、数据整理--连接、合并和重塑 之三:重塑和透视)
  • uniapp路由与页面跳转详解:API调用与Navigator组件实战
  • 如何使用腾讯云GPU云服务器自建一个简单的类似ChatGPT、Kimi的会话机器人
  • OpenCV与AI深度学习 | 基于YoloV11自定义数据集实现车辆事故检测(有源码,建议收藏!)
  • vue中如何关闭eslint检测?
  • 【子串分值——贡献法】
  • 软考:去中心化的部署有什么特点
  • vue2面试题6|[2024-11-11]
  • 25浙江省考-专项刷题(数字推理)-错题本
  • 从0开始学docker (每日更新 24-11-10)
  • Qt 项目架构设计