以向量加法为例介绍了CUDA程序得基本结构,使用xmake进行项目组织及编译。
相关代码:https://github.com/JilinLi4/LearningCUDA
在CUDA运行时API中,没有显示地初始化设备的函数。在第一次调用一个和设备管理及版本查询无关的运行时API函数时,设备将自动地初始化。
申请显存
cudaError_t cudaMalloc(void **address, size_t size);
释放显存
cudaError_t cudaFree(void* address);
cudaError_t cudaMemcpy(void *dst,const void *src,size_t count,enum cudaMemcpyKind kind
)
核函数中允许指派很多线程。因为一个GPU中有几千个计算核心,而总的线程数必须至少等于计算核心数才能充分利用GPU中的全部计算资源。
所以根据需要,在调用核函数时可以指定多个线程。比如:
func<<<2, 4>>>();
func指定了2个线程块,每个线程块中包含4个线程。总的线程数是 2×4=82 \times 4 = 82×4=8。也就是说,该程序中的核函数中代码执行方式是:“单指令-多线程”,即每一个线程都执行同一串指令。
线程组织结构是由调用函数配置的。如:
func<<>>();
一般来说:
将有关数据从主机传至设备之后,就可以调用核函数在设备中进行计算了。使用以下线程布局:
const int n = blockDim.x * blockIdx.x + threadIdx.x;
核函数没有使用参数N,当N是blockDim.x的整数倍时,不会引起问题,因为核函数的线程数刚好等于数组元素个数。然而,当N不是 blockDim.x的整数倍时,就有可能发生错误。例如将 N改为: 108+110^8 + 1108+1 而 block_size依然是128,如果griadSize的计算方式不变,那么必将有一个元素无法处理。所以我们应该将原来的计算方式:
gridSize=N/blockSize\text{gridSize} = N / \text{blockSize}gridSize=N/blockSize 改为
gridSize=(N+blockSize−1)/blockSize\text{gridSize} = (N + blockSize - 1) / \text{blockSize}gridSize=(N+blockSize−1)/blockSize
这样一来也会引入越界的风险,所以需要将线程索引的计算方式改为:
const int n = blockDim.x * blockIdx.x + threadIdx.x;
if(n >= N) return;
z[n] = x[n] + y[n];
double __device__ add1_device(const double x, const double y) {return x + y;
}void __global__ add1(const double *x, const double *y, double *z, const int N) {const int n = blockDim.x * blockIdx.x + threadIdx.x;if(n < N) {z[n] = add1_device(x[n], y[n]);}
}
void __device__ add2_device(const double x, const double y, double *z) {*z = *x + *y;
}void __global__ add1(const double *x, const double *y, double *z, const int N) {const int n = blockDim.x * blockIdx.x + threadIdx.x;if(n < N) {add1_device(x[n], y[n], &z[n]);}
}
void __device__ add3_device(const double x, const double y, double &z) {z = x + y;
}void __global__ add1(const double *x, const double *y, double *z, const int N) {const int n = blockDim.x * blockIdx.x + threadIdx.x;if(n < N) {add1_device(x[n], y[n], z[n]);}
}
bool checkRuntime(cudaError_t e, const char* call, int line, const char *file){if (e != cudaSuccess) {INFOE("CUDA Runtime error %s # %s, code = %s [ %d ] in file %s:%d", call, cudaGetErrorString(e), cudaGetErrorName(e), e, file, line);return false;}return true;}
错误信息实例:
CUDA Runtime error cudaMemcpy(d_x, h_x, M, cudaMemcpyDeviceToDevice) # invalid argument, code = cudaErrorInvalidValue [ 1 ] in file D:\work_dir\experiment\LearningCUDA\src\add.cu:29