cuda矩阵加法核函数需用global声明,线程索引必须边界检查:一维用int idx = blockidx.x * blockdim.x + threadidx.x; 二维用row/col计算并if(row
CUDA矩阵加法核函数怎么写才不出错
直接用
__global__函数实现两个同维矩阵的逐元素相加,最常踩的坑是线程索引越界和未同步。GPU线程按二维网格启动时,blockIdx、threadIdx和gridDim必须联合计算全局索引,且需做边界检查——哪怕矩阵尺寸能被 block size 整除,也不能省略判断。
- 每个线程处理一个输出元素,索引公式固定为:
int idx = blockIdx.x * blockDim.x + threadIdx.x;(一维启动)或int row = blockIdx.y * blockDim.y + threadIdx.y; int col = blockIdx.x * blockDim.x + threadIdx.x;(二维启动)- 必须加
if (row 判断,否则超出矩阵范围的线程会写脏内存- 核函数参数必须是原始指针(
float*),不能传std::vector或封装类;主机端分配的内存要用cudaMalloc,不是new主机端如何正确分配和拷贝矩阵内存
GPU显存和主机内存是分离的,
cudaMalloc分配的指针不能直接传给 CPU 代码用,cudaMemcpy方向稍错就会读到全零或崩溃。
- 先用
cudaMalloc(&d_A, size)在设备端分配三块内存:输入 A、B 和输出 C- 用
cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice)把主机数组拷过去;注意第三个参数是字节数,不是元素个数(常见错误:写成M*N而非M*N*sizeof(float))- 核函数执行完后,用
cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost)拷回结果,别漏掉这步——否则h_C还是初始化值- 最后调用
cudaFree释放设备内存,不释放会导致显存泄漏启动配置选
dim3还是简单一维?对矩阵加法这种规则二维结构,用二维
dim3启动更直观,但实际性能和一维没差别;关键在于 block size 选 16×16 还是 32×32,会影响 warp 利用率。
- 推荐用
dim3 blockSize(16, 16),对应 256 线程/块,是大多数 GPU 的最优选择(避免 warp 内部分支)- grid size 计算要向上取整:
dim3 gridSize((N + blockSize.x - 1) / blockSize.x, (M + blockSize.y - 1) / blockSize.y),不能简单写(N/16, M/16)- 如果坚持一维启动(如
blockSize(256)),则必须用单索引映射二维位置:int row = idx / N; int col = idx % N;,且确保idx为什么加了
cudaDeviceSynchronize()还报错核函数异步执行,不加同步就直接读结果,大概率拿到旧数据;但加了同步仍出错,通常是 CUDA 错误没检查,掩盖了真实问题。
- 每次 CUDA 调用后应检查错误:
cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) printf("CUDA error: %s\n", cudaGetErrorString(err));cudaDeviceSynchronize()放在核函数调用后、结果拷贝前,但若核函数本身有非法内存访问(比如越界写),它会阻塞并最终报cudaErrorLaunchFailure- 调试时可临时加
cuda-memcheck ./a.out运行,它能准确定位越界地址矩阵加法本身逻辑简单,真正耗时间的其实是内存搬运和错误排查——尤其是忘记
cudaFree或拷贝方向写反,这类问题不会编译报错,但结果永远不对。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!












