文章目录
- 1. kernel基本书写
- 2. grid-block设置
- 3. __device__ 使用
- 4. launch_bounds
- 5. kernel问题排查
- 6. CUDA_KERNEL_LOOP的使用
-
- 7. kernel中打印GPU数据
# 基本步骤
分配host内存,并进行数据初始化;
分配device内存,并从host将数据拷贝到device上;
调用CUDA的 kernel 函数在device上完成指定的运算;
将device上的运算结果拷贝到host上;
释放device和host上分配的内存。
2. grid-block设置
const int block_size = 128;
const int grid_size = (size + block_size - 1 ) / block_size;
dim3 grid_dim1, block_dim1(32, 32);
grid_dim1.x = (kNTotal + 32 - 1) / 32;
grid_dim1.y = (kMTotal + 32 - 1) / 32;
3. device 使用
template<typename T>
struct NonZeroOp
{__host__ __device__ __forceinline__ bool operator()(const T& a) const {return (a!=T(0));}
};
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
- 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 基本写法
- 注意基本写法index 通过循环来,这样保证一个block的thread读取的连续数据
#define CUDA_KERNEL_LOOP(i, n) \for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \i += blockDim.x * gridDim.x)
6.2 使用注意事项
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)
{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];atomicAdd(out + j * c + k, weight_ * cur_feat); }}}
}
- 当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);
}