You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
}

问题原因

  1. 直接访问设备内存触发段错误
    lastIndices是通过cudaMalloc分配的设备显存地址,CPU代码(打印循环)直接读写这个指针会触发段错误——CPU的虚拟地址空间和CUDA设备的显存地址空间完全独立,不能直接交叉访问。

  2. 缺乏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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 14:13:17