Matrix未在CUDA中成功从设备复制回主机

时间:2014-10-04 14:13:38

标签: cuda gpu

我是cuda的新手。我编写了一个内核来创建维度sizeXsize的单位矩阵(GPUsetIdentity)。在函数GPUfunctioncall中,我调用了我的内核。单位矩阵应存储在dDataInv中。但是当我将它复制回dataOut sizexsize时,所有值都为零。我知道,我在某个地方做了一些非常愚蠢的事情,但无法得到它,我是cuda的新手,如果有人能指出我的错误。谢谢。

#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <math.h>
#include <stdlib.h>
#include <iostream>
#include <stdlib.h>
#include <string>
#include <fstream>
#include <iterator>
#include <sstream>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <ctime>  
#include <stdlib.h>
#include <cuda_runtime.h>
#include "cuda.h"

#define BLOCKSIZE 16


using namespace std;

__global__ void GPUsetIdentity (float* matrix, int width)

{
        int tx = threadIdx.x;
        int bx = blockIdx.x;
        int offset = bx * BLOCKSIZE + tx;
        matrix[offset + width * offset] = 1;

}


void print_matrix_host(float* A , int nr_rows_A, int nr_cols_A) {

        for(int i = 0; i < nr_rows_A; ++i){
                for(int j = 0; j < nr_cols_A; ++j){
                        std::cout << A[i * nr_rows_A + j ]  << " ";
                }
                std::cout << std::endl;

        }
        std::cout << std::endl;
}

int GPUfunctioncall (float* hDataOut, int size){

        float *dDataInv;


        cudaMalloc ((void **) &dDataInv, size);
        cudaMemset ((void *) dDataInv, 0, size);



        dim3 idyThreads (BLOCKSIZE);
        dim3 idyBlocks (size / BLOCKSIZE);


        GPUsetIdentity <<< idyBlocks, idyThreads >>> (dDataInv, size);
        cudaThreadSynchronize ();

        cudaMemcpy ((void *) hDataOut, (void *) dDataInv, size, cudaMemcpyDeviceToHost);
        cudaFree (dDataInv);

        return 0;

}

int main()

{
        int size = 4;
        float* dataOut;

        dataOut = new float[size*size];

        GPUfunctioncall(dataOut, size);
        print_matrix_host(dataOut, size, size);


}

1 个答案:

答案 0 :(得分:1)

每当您遇到CUDA代码时遇到问题,最好使用proper cuda error checking。您还可以使用cuda-memcheck运行代码,以快速了解是否存在任何错误。

使用这些方法中的任何一种,您都会在内核启动时发现“无效的配置错误”。这通常意味着<<< >>>语法中的参数不正确。当您遇到这种类型的错误时,只需打印出这些值就可能表明存在问题。

在您的情况下,这行代码:

    dim3 idyBlocks (size / BLOCKSIZE);
0为4且idyBlocks为16时,

会导致size的值为BLOCKSIZE。因此,您要求内核启动0个非法的块。因此,您的内核未运行,结果不符合您的预期。

有多种方法可以解决这个问题,其中许多方法都涉及检测此情况,并在size无法被BLOCKSIZE整除时添加“额外阻止”。使用这种方法,我们可能会启动“额外线程”,因此我们必须在内核中包含一个“线程检查”,以防止那些额外的线程做任何事情(例如访问数组越界)。为此,我们经常需要知道内核中的预期大小,并且我们可以将此值作为额外的内核参数传递。

您在处理设备变量时也遇到了一些错误。以下代码:

    dataOut = new float[size*size];

为尺寸size的方阵矩阵分配足够的空间。但是以下代码:

    cudaMalloc ((void **) &dDataInv, size);

仅为size 字节分配足够的空间。您希望size*size*sizeof(float)代替size,而您希望在以下cudaMemsetcudaMemcpy操作中使用cudaMalloccudaMemsetcudaMemcpymalloc需要字节中的尺寸参数,就像memsetmemcpy和{{1}一样}。您在使用cudaMemsetcudaMemcpy时也会发现此错误。

以下代码有这些修改,似乎对我有效:

$ cat t580.cu
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#define BLOCKSIZE 16


using namespace std;

__global__ void GPUsetIdentity (float* matrix, int width, int size)

{
        int tx = threadIdx.x;
        int bx = blockIdx.x;
        int offset = bx * BLOCKSIZE + tx;
        if (tx < size)
          matrix[offset + width * offset] = 1;

}


void print_matrix_host(float* A , int nr_rows_A, int nr_cols_A) {

        for(int i = 0; i < nr_rows_A; ++i){
                for(int j = 0; j < nr_cols_A; ++j){
                        std::cout << A[i * nr_rows_A + j ]  << " ";
                }
                std::cout << std::endl;

        }
        std::cout << std::endl;
}

int GPUfunctioncall (float* hDataOut, int size){

        float *dDataInv;


        cudaMalloc ((void **) &dDataInv, size*size*sizeof(float));
        cudaMemset ((void *) dDataInv, 0, size*size*sizeof(float));



        dim3 idyThreads (BLOCKSIZE);
        int num_blocks = size/BLOCKSIZE + (size%BLOCKSIZE)?1:0;
        dim3 idyBlocks (num_blocks);


        GPUsetIdentity <<< idyBlocks, idyThreads >>> (dDataInv, size, size);
        cudaThreadSynchronize ();

        cudaMemcpy ((void *) hDataOut, (void *) dDataInv, size*size*sizeof(float), cudaMemcpyDeviceToHost);
        cudaFree (dDataInv);

        return 0;

}

int main()

{
        int size = 4;
        float* dataOut;

        dataOut = new float[size*size];

        GPUfunctioncall(dataOut, size);
        print_matrix_host(dataOut, size, size);


}
$ nvcc -arch=sm_20 -o t580 t580.cu
$ cuda-memcheck ./t580
========= CUDA-MEMCHECK
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

========= ERROR SUMMARY: 0 errors
$

请注意,将size两次传递给内核可能是多余的。对于这个特定的例子,我们可以很容易地使用width参数来进行内核“线程检查”。但出于教育目的,我选择将其作为单独的参数调用,因为在一般情况下,您通常会将其作为单独的参数传递给您编写的其他内核。

最后,请注意cudaThreadSynchronize() 已弃用,而应替换为cudaDeviceSynchronize()。在这个特定的例子中,niether实际上是必需的,因为下一个cudaMemcpy操作将强制进行相同类型的同步,但是如果你决定将cuda错误检查添加到你的代码中,你可以使用它(推荐)。