将数据从设备复制到主机时无效的参数错误

时间:2012-02-23 13:28:49

标签: arrays cuda copy

我在将数据从设备复制回主机时遇到问题。我的数据按结构排列:

typedef struct Array2D {
    double* arr;        
    int rows;       
    int cols;       
} Array2D;

arr是一个'扁平'数组。 rowscols描述了维度。

下面的代码显示了我如何将数据复制回主机:

h_output = (Array2D*) malloc(sizeof(Array2D));
cudaMemcpy(h_output, d_output, sizeof(Array2D), cudaMemcpyDeviceToHost);
double* h_arr = (double*) malloc(h_output->cols*h_output->rows*sizeof(double));
cudaMemcpy(h_arr, h_output->arr, h_output->cols*h_output->rows*sizeof(double), cudaMemcpyDeviceToHost);
h_output->arr = h_arr;

然而,在第四行中,执行失败,cuda错误11(无效参数)。我不明白为什么会这样。数组的大小是正确的,我可以从主机访问h_outputh_array,并且都有“真实”地址。

修改 的 很抱歉对请求的延迟回复提供了更多信息(=更多代码)。

我通过尝试访问主机上设备指针的值来测试指针d_output->arr是设备指针。正如所料,我不被允许这样做,让我认为d_output->arr实际上是一个有效的设备指针。

该代码的目标是使用四阶Runge-Kutta方法求解Thiele的微分方程。

class CalculationSpecification
{

    /* FUNCTIONS OMITTED */

public:
    __device__ void RK4_n(CalculationSpecification* cs, CalcData data, Array2D* d_output)
    {
        double* rk4data = (double*)malloc((data.pdata->endYear - data.pdata->startYear + 1)*data.pdata->states*sizeof(double));

        /* CALCULATION STUFF HAPPENS HERE */

        // We know that rows = 51, cols = 1 and that rk4data contains 51 values as it should.
        // This was confirmed by using printf directly in this function.
        d_output->arr = rk4data;
        d_output->rows = data.pdata->endYear - data.pdata->startYear + 1;
        d_output->cols = data.pdata->states;
    }
};


class PureEndowment : CalculationSpecification
{
    /* FUNCTIONS OMITTED */

public:
    __device__ void Compute(Array2D *result, CalcData data)
    {
        RK4_n(this, data, result);
    }
};


__global__ void kernel2(Array2D *d_output)
{
    /* Other code that initializes 'cd'. */
    PureEndowment pe;
    pe.Compute(d_output,cd);
}


void prepareOutputSet(Array2D* h_output, Array2D* d_output, int count)
{
    h_output = (Array2D*) malloc(sizeof(Array2D));
    cudaMemcpy(h_output, d_output, sizeof(Array2D), cudaMemcpyDeviceToHost); // After this call I can read the correct values of row, col as well as the address of the pointer.
    double* h_arr = (double*) malloc(h_output->cols*h_output->rows*sizeof(double));
    cudaMemcpy(h_arr, h_output->arr, h_output->cols*h_output->rows*sizeof(double), cudaMemcpyDeviceToHost)
    h_output->arr = h_arr;
}

int main()
{
    Array2D *h_output, *d_output;
    cudaMalloc((void**)&d_output, sizeof(Array2D));

    kernel2<<<1,1>>>(d_output);
    cudaDeviceSynchronize();

    prepareOutputSet(h_output, d_output, 1);

    getchar();
    return 0;
}

EDIT2

此外,我现在已经测试过,d_output->arr在设备上运行后的价值与h_output->arr之后cudaMemcpy的价值相同 - prepareOutputSet

4 个答案:

答案 0 :(得分:2)

这(使用cudaMemcpy复制设备分配的内存)是CUDA 4.1中的已知限制。修复工作正在进行中,将在未来版本的CUDA运行时中发布。

答案 1 :(得分:0)

您看到的错误几乎肯定是由于h_output->arr不是有效的设备指针,或h_output->rowsh_output->cols以某种方式具有不正确的值。您已选择不显示任何代码来说明源内存d_output的内容是如何设置的,因此无法确定问题的根本原因是什么。

为了说明这一点,这是一个完整的,可运行的演示,显示了已发布的代码:

#include <cstdlib>
#include <cstdio>

inline void GPUassert(cudaError_t code, char * file, int line, bool Abort=true)
{
    if (code != 0) {
        fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code),file,line);
        if (Abort) exit(code);
    }       
}

#define GPUerrchk(ans) { GPUassert((ans), __FILE__, __LINE__); }

typedef float Real;

typedef struct Array2D {
    Real* arr;        
    int rows;       
    int cols;       
} Array2D;

__global__ void kernel(const int m, const int n, Real *lval, Array2D *output)
{
    lval[threadIdx.x] = 1.0f + threadIdx.x;
    if (threadIdx.x == 0) {
        output->arr = lval;
        output->rows = m;
        output->cols = n;
    }
}

int main(void)
{
    const int m=8, n=8, mn=m*n;

    Array2D *d_output;
    Real *d_arr;
    GPUerrchk( cudaMalloc((void **)&d_arr,sizeof(Real)*size_t(mn)) ); 

    GPUerrchk( cudaMalloc((void **)&d_output, sizeof(Array2D)) );
    kernel<<<1,mn>>>(m,n,d_arr,d_output);
    GPUerrchk( cudaPeekAtLastError() );

    // This section of code is the same as the original question
    Array2D *h_output = (Array2D*)malloc(sizeof(Array2D));
    GPUerrchk( cudaMemcpy(h_output, d_output, sizeof(Array2D), cudaMemcpyDeviceToHost) );
    size_t sz = size_t(h_output->rows*h_output->cols)*sizeof(Real);
    Real *h_arr = (Real*)malloc(sz);
    GPUerrchk( cudaMemcpy(h_arr, h_output->arr, sz, cudaMemcpyDeviceToHost) );

    for(int i=0; i<h_output->rows; i++)
        for(int j=0; j<h_output->cols; j++)
            fprintf(stdout,"(%d %d) %f\n", i, j, h_arr[j + i*h_output->rows]);

    return 0;
}

我不得不在这里采取一些自由,因为我只有一个计算能力1.2设备,所以没有设备端malloc,没有双精度。但是从设备内存中检索有效Array2D结构并使用其内容的主机端API调用实际上是相同的。运行程序按预期工作:

$ nvcc -Xptxas="-v" -arch=sm_12 Array2D.cu 
ptxas info    : Compiling entry function '_Z6kerneliiPfP7Array2D' for 'sm_12'
ptxas info    : Used 2 registers, 16+16 bytes smem

$ cuda-memcheck ./a.out 
========= CUDA-MEMCHECK
(0 0) 1.000000
(0 1) 2.000000
(0 2) 3.000000
(0 3) 4.000000
(0 4) 5.000000
(0 5) 6.000000
(0 6) 7.000000
(0 7) 8.000000
(1 0) 9.000000
(1 1) 10.000000
(1 2) 11.000000
(1 3) 12.000000
(1 4) 13.000000
(1 5) 14.000000
(1 6) 15.000000
(1 7) 16.000000
(2 0) 17.000000
(2 1) 18.000000
(2 2) 19.000000
(2 3) 20.000000
(2 4) 21.000000
(2 5) 22.000000
(2 6) 23.000000
(2 7) 24.000000
(3 0) 25.000000
(3 1) 26.000000
(3 2) 27.000000
(3 3) 28.000000
(3 4) 29.000000
(3 5) 30.000000
(3 6) 31.000000
(3 7) 32.000000
(4 0) 33.000000
(4 1) 34.000000
(4 2) 35.000000
(4 3) 36.000000
(4 4) 37.000000
(4 5) 38.000000
(4 6) 39.000000
(4 7) 40.000000
(5 0) 41.000000
(5 1) 42.000000
(5 2) 43.000000
(5 3) 44.000000
(5 4) 45.000000
(5 5) 46.000000
(5 6) 47.000000
(5 7) 48.000000
(6 0) 49.000000
(6 1) 50.000000
(6 2) 51.000000
(6 3) 52.000000
(6 4) 53.000000
(6 5) 54.000000
(6 6) 55.000000
(6 7) 56.000000
(7 0) 57.000000
(7 1) 58.000000
(7 2) 59.000000
(7 3) 60.000000
(7 4) 61.000000
(7 5) 62.000000
(7 6) 63.000000
(7 7) 64.000000
========= ERROR SUMMARY: 0 errors

答案 2 :(得分:0)

我尝试使用cudaMalloc在主机上分配指针Array2D->arr,而不是使用malloc在设备上分配它。之后,代码按预期工作。

它看起来非常像nVidia论坛中线程(http://forums.nvidia.com/index.php?showtopic=222659)中描述的问题,Pavan在对该问题的评论中提到了这个问题。

我认为现在可能会关闭这个问题,因为代码运行正常。但是,如果有人提议在设备上使用malloc的解决方案,请随时发布。

答案 3 :(得分:-1)

看起来h_output分配了对malloc()的调用。在第一次调用cudaMemcpy()(第2行)时,h_output被用作主机指针(看起来正确)。在第二次调用cudaMemcpy()(第4行)时,h_output->arr被用作设备指针(似乎不正确)。在第4行中,看起来您正在从主机内存复制到主机内存。因此,您可能只想使用memcpy()而不是cudaMemcpy()

至少从你提供的代码看起来就是这样。