CUDA全局内存分配初始化数组遇段错误,添加打印后问题复现
void CudaRenderer::render() { dim3 blockDim(16, 16, 1); dim3 gridDim((image->width + blockDim.x - 1) / blockDim.x, (image->height + blockDim.y - 1) / blockDim.y); int *filteredCircles, *lastIndices, sz = gridDim.y * gridDim.x ; cudaMalloc((void **)&filteredCircles, sizeof(int) * sz * 2000); cudaMalloc((void **)&lastIndices, sizeof(int) * sz); cudaMemset(lastIndices, 0, sizeof(int) * sz); filterCircles<<<gridDim, blockDim>>>(filteredCircles, lastIndices); for (int i = 0; i < 10; ++i) printf("lastIndices[%d] = %d\n", i, lastIndices[i]); kernelRenderCircles<<<gridDim, blockDim>>>(filteredCircles, lastIndices); cudaFree(filteredCircles); cudaDeviceSynchronize(); }
问题原因
直接访问设备内存触发段错误
lastIndices是通过cudaMalloc分配的设备显存地址,CPU代码(打印循环)直接读写这个指针会触发段错误——CPU的虚拟地址空间和CUDA设备的显存地址空间完全独立,不能直接交叉访问。缺乏CUDA错误检查导致结果异常
未添加打印时程序编译正常但输出错误,大概率是filterCircles或kernelRenderCircles核函数执行出错(比如内存越界、线程索引计算错误),但你没有检查任何CUDA API调用和核函数的执行状态,导致错误被隐藏,无法定位根源。
修复步骤
1. 修正段错误:将设备数据拷贝到主机再读取
要查看lastIndices的值,必须先用cudaMemcpy把设备内存的数据复制到主机内存,再在CPU端打印:
void CudaRenderer::render() { dim3 blockDim(16, 16, 1); dim3 gridDim((image->width + blockDim.x - 1) / blockDim.x, (image->height + blockDim.y - 1) / blockDim.y); int *filteredCircles, *lastIndices, sz = gridDim.y * gridDim.x ; cudaMalloc((void **)&filteredCircles, sizeof(int) * sz * 2000); cudaMalloc((void **)&lastIndices, sizeof(int) * sz); cudaMemset(lastIndices, 0, sizeof(int) * sz); filterCircles<<<gridDim, blockDim>>>(filteredCircles, lastIndices); // 新增:从设备拷贝数据到主机 int* host_lastIndices = (int*)malloc(sizeof(int) * sz); cudaMemcpy(host_lastIndices, lastIndices, sizeof(int)*sz, cudaMemcpyDeviceToHost); // 打印主机内存中的数据 for (int i = 0; i < 10; ++i) printf("lastIndices[%d] = %d\n", i, host_lastIndices[i]); // 释放主机内存 free(host_lastIndices); kernelRenderCircles<<<gridDim, blockDim>>>(filteredCircles, lastIndices); // 先同步确保核函数执行完毕,再释放内存 cudaDeviceSynchronize(); cudaFree(filteredCircles); cudaFree(lastIndices); }
2. 添加CUDA错误检查,定位结果异常问题
给所有CUDA操作(API调用、核函数启动)添加错误检查,能快速定位执行错误:
void CudaRenderer::render() { cudaError_t err; dim3 blockDim(16, 16, 1); dim3 gridDim((image->width + blockDim.x - 1) / blockDim.x, (image->height + blockDim.y - 1) / blockDim.y); int *filteredCircles, *lastIndices, sz = gridDim.y * gridDim.x ; // 检查cudaMalloc err = cudaMalloc((void **)&filteredCircles, sizeof(int) * sz * 2000); if(err != cudaSuccess) { printf("cudaMalloc filteredCircles failed: %s\n", cudaGetErrorString(err)); return; } err = cudaMalloc((void **)&lastIndices, sizeof(int) * sz); if(err != cudaSuccess) { printf("cudaMalloc lastIndices failed: %s\n", cudaGetErrorString(err)); cudaFree(filteredCircles); return; } err = cudaMemset(lastIndices, 0, sizeof(int) * sz); if(err != cudaSuccess) { printf("cudaMemset failed: %s\n", cudaGetErrorString(err)); cudaFree(filteredCircles); cudaFree(lastIndices); return; } // 启动核函数并检查错误 filterCircles<<<gridDim, blockDim>>>(filteredCircles, lastIndices); err = cudaGetLastError(); if(err != cudaSuccess) { printf("filterCircles kernel launch failed: %s\n", cudaGetErrorString(err)); cudaFree(filteredCircles); cudaFree(lastIndices); return; } // 同步设备,检查核函数执行阶段的错误 err = cudaDeviceSynchronize(); if(err != cudaSuccess) { printf("filterCircles kernel execution failed: %s\n", cudaGetErrorString(err)); cudaFree(filteredCircles); cudaFree(lastIndices); return; } // 拷贝数据到主机并打印 int* host_lastIndices = (int*)malloc(sizeof(int) * sz); err = cudaMemcpy(host_lastIndices, lastIndices, sizeof(int)*sz, cudaMemcpyDeviceToHost); if(err != cudaSuccess) { printf("cudaMemcpy to host failed: %s\n", cudaGetErrorString(err)); free(host_lastIndices); cudaFree(filteredCircles); cudaFree(lastIndices); return; } for (int i = 0; i < 10; ++i) printf("lastIndices[%d] = %d\n", i, host_lastIndices[i]); free(host_lastIndices); // 检查render核函数 kernelRenderCircles<<<gridDim, blockDim>>>(filteredCircles, lastIndices); err = cudaGetLastError(); if(err != cudaSuccess) { printf("kernelRenderCircles launch failed: %s\n", cudaGetErrorString(err)); cudaFree(filteredCircles); cudaFree(lastIndices); return; } err = cudaDeviceSynchronize(); if(err != cudaSuccess) { printf("kernelRenderCircles execution failed: %s\n", cudaGetErrorString(err)); cudaFree(filteredCircles); cudaFree(lastIndices); return; } cudaFree(filteredCircles); cudaFree(lastIndices); }
额外注意点
cudaDeviceSynchronize()要放在核函数执行之后、内存释放之前,确保核函数完全执行完毕再释放设备内存,避免野指针问题。- 核函数中要检查线程索引是否越界,比如
filterCircles中计算全局索引时,要确保不超过sz的范围,否则会导致设备内存越界,破坏数据甚至触发错误。
内容的提问来源于stack exchange,提问作者Bbbbbunn2023
相关产品推荐
相关产品推荐

