复制包含指向设备的2d指针的结构

时间:2013-11-13 12:25:25

标签: c++ visual-studio-2010 cuda

我有一个问题 - 涉及从主机复制包含指向设备的2D指针的结构,我的代码如下

struct mymatrix
    {
        matrix m;
        int x;
    };
size_t pitch;

mymatrix m_h[5];
for(int i=0; i<5;i++){
    m_h[i].m = (float**) malloc(4 * sizeof(float*));  
       for (int idx = 0; idx < 4; ++idx)
           {
               m_h[i].m[idx] = (float*)malloc(4 * sizeof(float));
           }
       }
mymatrix *m_hh = (mymatrix*)malloc(5*sizeof(mymatrix));
memcpy(m_hh,m_h,5*sizeof(mymatrix));

for(int i=0 ; i<5 ;i++) 
{
     cudaMallocPitch((void**)&(m_hh[i].m),&pitch,4*sizeof(float),4);
     cudaMemcpy2D(m_hh[i].m, pitch, m_h[i].m, 4*sizeof(float), 4*sizeof(float),4,cudaMemcpyHostToDevice);
}
mymatrix *m_d;
cudaMalloc((void**)&m_d,5*sizeof(mymatrix));
cudaMemcpy(m_d,m_hh,5*sizeof(mymatrix),cudaMemcpyHostToDevice);
distance_calculation_begins<<<1,16>>>(m_d,pitch);

问题

使用此代码,我无法访问结构的2D指针元素,但我可以从设备中的该结构访问x。例如例如,如果我初始化

,我会收到指针mymatrix* m的m_d
m[0].m[0][0] = 5;

并打印此值,例如

cuPrintf("The value is %f",m[0].m[0][0]);

在设备中,我没有输出。意味着我无法使用2D指针,但如果我尝试访问

 m[0].x = 5; 

然后我就可以打印出来了。我认为我的初始化是正确的,但我无法弄清楚问题。任何人的帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

根据您在主机上初始化它的方式,您的matrix m类/结构成员似乎是某种双指针:

    m_h[i].m = (float**) malloc(4 * sizeof(float*)); 

在主机和设备之间复制具有嵌入式指针的结构数组在某种程度上是有用的。复制双指针指向的数据结构也很复杂。

有关嵌入指针的结构数组,请参阅this posting

要复制2D数组(双指针,即**),请参阅this posting。我们不会使用cudaMallocPitch / cudaMemcpy2D来完成此操作。 (注意cudaMemcpy2D采用单指针*参数,你传递双指针**参数,例如m_h[i].m

除了上述方法之外,建议您压平数据,以便可以使用单指针引用来引用它,而不使用嵌入指针。

答案 1 :(得分:1)

除了@RobertCrovella在您的代码中注明的问题之外,还要注意:

  • 您只会使用memcpym_h复制到m_hh来获得结构的浅层副本。
  • 您假设pitchcudaMemcpy2D()的所有调用都相同(您覆盖音高并在结尾处仅使用最新副本)。我认为现在可能是安全的假设,但将来可能会改变。
  • 您正在使用cudaMemcpyHostToDevice()cudaMemcpyHostToDevice复制到m_hh,该a[10][20][30] = 3 位于主机上,而不是设备上。

使用许多小缓冲区和指针表在CUDA中效率不高。小的分配和解除分配最终会占用大量时间。此外,使用指针表会导致额外的内存事务,因为必须先从内存中检索指针,然后才能将它们用作索引的基础。所以,如果你考虑这样的结构:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>

typedef float* mymatrix;

const int n_matrixes(5);
const int w(4);
const int h(4);


#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
   if (code != cudaSuccess) 
   {
      fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
      if (abort) exit(code);
   }
}

__global__ void test(mymatrix m_d, size_t pitch_floats)
{
  // Print the value at [2][3][4].
  printf("%f ", m_d[3 + (2 * h + 4) * pitch_floats]);
}


int main()
{
  mymatrix m_h;
  gpuErrchk(cudaMallocHost(&m_h, n_matrixes * w * sizeof(float) * h));
  // Set the value at [2][3][4].
  m_h[2 * (w * h) + 3 + 4 * w] = 5.0f;

  // Create a device copy of the matrix.
  mymatrix m_d;
  size_t pitch;
  gpuErrchk(cudaMallocPitch((void**)&m_d, &pitch, w * sizeof(float), n_matrixes * h));
  gpuErrchk(cudaMemcpy2D(m_d, pitch, m_h, w * sizeof(float), w * sizeof(float), n_matrixes * h, cudaMemcpyHostToDevice));

  test<<<1,1>>>(m_d, pitch / sizeof(float));

  gpuErrchk(cudaPeekAtLastError());
  gpuErrchk(cudaDeviceSynchronize());
}

必须首先从内存中检索[10]处的指针,导致您的扭曲被搁置很长时间(费米最多约600个循环)。然后,对于第二个指针发生同样的事情,再添加600个周期。此外,这些请求不太可能合并,导致更多的内存事务。

正如罗伯特所说,解决方案是平整你的记忆结构。我已经为此添加了一个示例,您可以将其作为程序的基础。如您所见,代码总体上要简单得多。确实变得有点复杂的部分是指数计算。此外,这种方法假设您的矩阵都具有相同的大小。

我也添加了错误检查。如果您在代码中添加了错误检查,那么您至少会发现一些错误而不需要额外的努力。

{{1}}