2012-02-23 29 views
3

我在将设备中的数据复制回主机时遇到问题。我的数据排列在一个结构中:将数据从设备复制到主机时出现无效参数错误

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,并且都有“真实”地址。

EDIT 对不起,为更多的信息(=更多的代码)的请求后期应答。

我测试过,指针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->arrprepareOutputSet第一cudaMemcpy -call后的值。

+1

错误的最可能的来源是'houtput-> arr'不是一个有效的设备指针。你能扩展一下你的代码来展示你如何分配和拷贝'd_output'的内容给设备吗? – talonmies 2012-02-23 13:36:13

+0

'd_output'及其内容使用'malloc()'在设备上分配。我确信它包含实际的数据,因为我试着打印'd_output-> arr'的内容并获得了预期的输出。 – ssnielsen 2012-02-23 13:55:05

+0

你的意思是'h_output' _及其内容_?因为'd_output'没有出现在你的示例代码中。 – pQB 2012-02-23 15:06:14

回答

2

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

+0

听到这个很高兴。 – ssnielsen 2012-03-08 08:56:55

+0

@harrism:我得到了相同的错误代码。在进一步的调查中,我发现没有足够的RAM将数据复制回CPU。因此,'cudaMemcpy'失败了。这是错误代码的有效原因还是我错误的方式? – Programmer 2012-12-23 17:04:40

+0

这是与此处所述不同的问题。 – harrism 2013-01-03 00:53:08

-1

看起来像h_output分配给malloc()的呼叫。在cudaMemcpy()(第2行)的第一次调用中,h_output被用作主机指针(这看起来是正确的)。在cudaMemcpy()(第4行)的第二次调用中,h_output->arr被用作设备指针(这看起来不正确)。在第四行中,它看起来像是从主机内存复制到主机内存。因此,您可能只想使用直接memcpy()而不是cudaMemcpy()

至少这是您提供的代码的外观。

+0

这实际上并不是代码所做的。它应该是完全正确的,但是*只有*如果'h_output-> arr'(和扩展名为'd_output-> arr'是源内存)保存有效的设备指针。 – talonmies 2012-02-23 15:15:29

+0

我不确定我关注。 'h_output'被分配'malloc()'。然后用'cudaMemcpy()'将它作为主机指针复制到它中。然后(不需要将'h_output-> arr'设置为从前一次调用到'cudaMemcpy()'的设置),''cudaMemcpy()'会再次使用'h_output-> arr'作为设备指针调用。看来你对这个问题发表了一个评论。 – 2012-02-23 16:27:59

+0

''cudaMemcpy()'调用后,'h_output'实际上指向'd_output'指向的设备内存结构的副本。如果'd_output-> arr'是一个有效的设备指针,复制后的'h_output-> arr'也是如此。 – talonmies 2012-02-23 17:18:18

0

您看到的错误几乎肯定是由于h_output->arr不是有效的设备指针,或者由h_output->rowsh_output->cols以某种方式具有不正确的值而造成的。你已经选择不显示任何代码来解释如何设置源内存的内容,所以不可能肯定地说你的问题的根本原因是什么。

为了说明这一点,这里是表示动作的代码发布一个完整的,可运行的演示:

#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 
+0

原始问题现在用更多的代码更新。希望对问题有所了解。 – ssnielsen 2012-02-24 13:06:04

+0

您发布的额外代码不会改变此答案的结论 - 它清楚地表明,如果所讨论的指针是有效的设备指针,那么您发布的原始API代码已更正。真正的问题是为什么你的设备malloced指针在返回主机的时候是无效的。 – talonmies 2012-02-25 11:06:23

0

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

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

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

相关问题