从设备复制回主机时如何修复cudaError 77

时间:2019-08-08 14:31:30

标签: cuda runtime-error

我正在编写一个简单的示例程序来测试memCpy和较大程序的内核运行并发性。在编写此示例时,我偶然发现了错误77,即cudaErrorIllegalAddress。

我读到某个地方,它来自内核访问无效地址,而不是memcpy本身。所以我尝试索引输入数组的最低元素(0)。错误仍然存​​在。

由于它只是一个小示例程序,我将提供整个代码;

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>
#include <stdlib.h>

#define BLOCKS 32
#define THREADS 16

__global__ void kernel(double *d_in, double *d_out) {
    int index = threadIdx.x + blockDim.x * blockIdx.x;
    d_out[index] = d_in[index] + 5;
}

int main() {
    const int GPU_N = 2;
    const int data_size = 2048;
    const int cycles = 2;

    double *h_in, *h_out, *d_in, *d_out;

    h_in = (double*)malloc(sizeof(double) * data_size);
    h_out = (double*)malloc(sizeof(double) * data_size);

    for (int i = 0; i < data_size; i++) {
        h_in[i] = 21;
    }

    cudaError_t error;

    printf("1\n");
    for (int i = 0; i < cycles; i++) {
        //cuMalloc
        for (int j = 0; j < GPU_N; j++) {
            cudaSetDevice(j);
            cudaMalloc((void**)&d_in, sizeof(double) * data_size / 4);
            cudaMalloc((void**)&d_out, sizeof(double) * data_size / 4);

            printf("2\n");
        }

        for (int j = 0; j < GPU_N; j++) {
            cudaSetDevice(j);
            cudaMemcpyAsync(d_in, h_in, sizeof(double) * data_size / 4, cudaMemcpyHostToDevice);
            printf("3\n");
        }

        for (int j = 0; j < GPU_N; j++) {
            cudaSetDevice(j);
            kernel<<< BLOCKS, THREADS, 0, 0 >>>(d_in, d_out);
            error = cudaGetLastError();
            printf("4\n");
        }

        for (int j = 0; j < GPU_N; j++) {
            cudaSetDevice(j);
            error = cudaMemcpyAsync(h_out, d_out, sizeof(double) * data_size / 4, cudaMemcpyDeviceToHost);
            printf("D2H %i\n", error);
            printf("5\n");
        }

        for (int j = 0; j < GPU_N; j++) {
            cudaSetDevice(j);
            cudaFree(d_in);
            cudaFree(d_out);
            printf("6\n");
        }
    }

    for (int i = 0; i < data_size; i++) {
        printf("%i\n", h_out[i]);
    }

    getchar();

}

所以输出应该类似于:

1
1
2
2
3
3
4
4
5
5
6
6
1
1
2
2
3
3
4
4
5
5
6
6
26
26
26
26
26
.....

,然后显示垃圾邮件。这样做直到需要打印5为止,然后输出error 77。另外,结果的输出不是预期的26,而是-842150451

1 个答案:

答案 0 :(得分:3)

此代码存在几个问题。

  1. 正如评论中已经指出的那样,此处的printf格式说明符(%i)是错误的:

    printf("%i\n", h_out[i]);
    

    要打印的数量是double个数量,适当的格式说明符应该是%f

  2. 此代码将不起作用(对于GPU_N大于1):

    for (int j = 0; j < GPU_N; j++) {
        cudaSetDevice(j);
        cudaMalloc((void**)&d_in, sizeof(double) * data_size / 4);
        cudaMalloc((void**)&d_out, sizeof(double) * data_size / 4);
    
        printf("2\n");
    }
    

    d_ind_out是单个变量。您不必以某种方式重用它们。当此循环经过第2次(或更高版本)迭代时,它将覆盖先前分配的指针值。稍后,这将导致代码麻烦,因为对于您的至少一个内核启动,您将传递指向不在该特定GPU上驻留的数据的指针(问题的这一特定方面是错误的最接近原因77报告。)

    一种解决方案是提供指针数组以使其工作。

  3. 您在循环中发出的某些CUDA活动可能是异步的。因此,为确保最终h_out的打印输出显示预期的结果,您应该等待GPU上的所有工作完成。实现此目的的一种方法是对cudaDeviceSynchronize()的另一组调用。 (我不想争论cudaFree是否是异步的。我认为这是一个明智的建议,值得注意。如果您觉得可以跳过此项目,请按照自己的意愿做。出于学习目的,我由于下面的注释中指出的原因,此项对于获得特定代码的预期结果不是必需/强制性的。这个答案并不旨在成为异步工作发行的完整论述。为此,我建议进一步研究cuda标签上的任何相关问题,和/或研究相关的CUDA示例代码。

这是经过修改的代码,可以解决上述问题(我缩短了最终的打印输出循环):

$ cat t1477.cu
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>
#include <stdlib.h>

#define BLOCKS 32
#define THREADS 16

__global__ void kernel(double *d_in, double *d_out) {
    int index = threadIdx.x + blockDim.x * blockIdx.x;
    d_out[index] = d_in[index] + 5;
}

int main() {
    const int GPU_N = 2;
    const int data_size = 2048;
    const int cycles = 2;

    double *h_in, *h_out, *d_in[GPU_N], *d_out[GPU_N];

    h_in = (double*)malloc(sizeof(double) * data_size);
    h_out = (double*)malloc(sizeof(double) * data_size);

    for (int i = 0; i < data_size; i++) {
        h_in[i] = 21;
    }

    cudaError_t error;

    printf("1\n");
    for (int i = 0; i < cycles; i++) {
        //cuMalloc
        for (int j = 0; j < GPU_N; j++) {
            cudaSetDevice(j);
            cudaMalloc((void**)(&(d_in[j])), sizeof(double) * data_size / 4);
            cudaMalloc((void**)(&(d_out[j])), sizeof(double) * data_size / 4);

            printf("2\n");
        }

        for (int j = 0; j < GPU_N; j++) {
            cudaSetDevice(j);
            cudaMemcpyAsync(d_in[j], h_in, sizeof(double) * data_size / 4, cudaMemcpyHostToDevice);
            printf("3\n");
        }

        for (int j = 0; j < GPU_N; j++) {
            cudaSetDevice(j);
            kernel<<< BLOCKS, THREADS, 0, 0 >>>(d_in[j], d_out[j]);
            error = cudaGetLastError();
            printf("4\n");
        }

        for (int j = 0; j < GPU_N; j++) {
            cudaSetDevice(j);
            error = cudaMemcpyAsync(h_out, d_out[j], sizeof(double) * data_size / 4, cudaMemcpyDeviceToHost);
            printf("D2H %i\n", error);
            printf("5\n");
        }

        for (int j = 0; j < GPU_N; j++) {
            cudaSetDevice(j);
            cudaFree(d_in[j]);
            cudaFree(d_out[j]);
            printf("6\n");
        }
    }
    for (int i = 0; i < GPU_N; i++){
        cudaSetDevice(i);
        cudaDeviceSynchronize();}
    for (int i = 0; i < 10; i++) {
        printf("%f\n", h_out[i]);
    }


}
$ nvcc -o t1477 t1477.cu
$ cuda-memcheck ./t1477
========= CUDA-MEMCHECK
1
2
2
3
3
4
4
D2H 0
5
D2H 0
5
6
6
2
2
3
3
4
4
D2H 0
5
D2H 0
5
6
6
26.000000
26.000000
26.000000
26.000000
26.000000
26.000000
26.000000
26.000000
26.000000
26.000000
========= ERROR SUMMARY: 0 errors
$