穷且益坚 不坠青云之志
1 前言与想法
CUDA编程还有以下内容需要整:
- 排序的样本排序、双调排序网络和奇偶排序;
- 矩阵转置(访存密集型讨论);
- 前缀和;
- 归约(可以写短点,前面多少涉及过);
- thrust、CUBLAS、cuDNN等库的使用;
- profiler、nsight、CUDA GDB的使用;
- 回头看;
这么一看,从数量上,也到一半了。
卷积是很经典的图像处理的方法了,很好理解,我想实现一个贴近应用的卷积,即可设置步长(一般是偶数),核一般是奇数,我见过的的卷积核有:15 8,7 4,5 2,3 2,3 1
全程都使用增广法,判断不了一点!
实现以下几个版本:
- CPU版;
- 全局内存版本(1D线程);
- 核常量版本(1D线程);
- 核常量,共享内存版本(2D线程);
顺带提一嘴,优化想法并非总是有效,因为牵一发而动全身,线程数量与映射方式、共存、寄存器、全局存储,彼此有联系。L1和L2缓存的加入,有时让共存很尴尬。外带编译器自带的优化,啥时候变了我都不懂!
还有,矩阵乘法已经可以用硬件实现了,那什么Tensor Core。不会没事,以后肯定不让你写这个
2 代码
2.1 预定义
#define MAT_SIZE 640 // 统一方阵
#define KERNEL_SIZE 7 // 常见的最大尺寸
#define STRIDE 4
#define ELEM_TYPE float
#define BLOCK_DIM 256
#define GRID_DIM 64
#define BLOCK_DIM_X 16
#define BLOCK_DIM_Y BLOCK_DIM_X
#define GRID_DIM_X 8
#define GRID_DIM_Y GRID_DIM_X
#define SHARED_SIZE ((BLOCK_DIM_X-1)*STRIDE+1+KERNEL_SIZE/2*2)
#define CUDA_CALL(x) do { if((x) != cudaSuccess) { printf("Error at %s:%d\n %s \n",__FILE__,__LINE__,cudaGetErrorString((x))); return EXIT_FAILURE;}} while(0)
2.2 CPU版本
兄弟,咱保证就用指针好不好?
// cpu 增广卷积 带步长
ELEM_TYPE* cpu_conv_2d(ELEM_TYPE* mat, ELEM_TYPE* kernel, int n, int size_out, int kernel_size, int stride) {
int n1 = n+kernel_size/2*2;
ELEM_TYPE* ret = new ELEM_TYPE[size_out*size_out];
// 外循环,两层
for (int i=0; i<n; i+=stride) {
for (int j=0; j<n; j+=stride) {
// 内循环,两层,二维嘛