CUDA阵列结构数组(AoSoA)

时间:2012-03-09 19:47:03

标签: c arrays struct cuda malloc

注4
所以代码终于修好了!原来最后的问题是我将分配给每个数组的空间大小添加到ptr中,但是c已经考虑了变量的大小,所以我本质上增加了4x的字节空间,因为我应该有因此,只显示5元素阵列中的前两个元素。 AoSoA现在已全面运作。小心你的记忆。管理如果你尝试类似的东西,我会遇到很多看似愚蠢的错误,因为我的初始代码很草率。

请注意:
+不正确的偏移量 +不必要的malloc&#39 +超出范围参考

以下是工作示例代码,结果如下!

#include <stdio.h>

#define REGIONS 20
#define YEARS 5

__inline __host__ 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);
   }
}

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }

struct AnimalPopulationForYear_s
{
   bool isYearEven;
   int * rabbits;
   int * hyenas;
};

AnimalPopulationForYear_s * dev_pop;

__global__ void RunSim(AnimalPopulationForYear_s dev_pop[],
               int year)
{
   int idx = blockIdx.x*blockDim.x+threadIdx.x;
   int rabbits, hyenas;
   int arrEl = year-1;

   rabbits = (idx+1) * year * year; 
   hyenas = rabbits / 10;

   if ( rabbits > 100000 ) rabbits = 100000;   
   if ( hyenas < 2 ) hyenas = 2;

   if ( idx < REGIONS ) dev_pop[arrEl].rabbits[idx] = rabbits;
   if ( idx < REGIONS ) dev_pop[arrEl].hyenas[idx] = hyenas;

   if (threadIdx.x == 0 && blockIdx.x == 0)
      dev_pop[arrEl].isYearEven = (year & 0x01 == 0x0);
}

int main()
{
   //Various reused sizes...
   const size_t fullArrSz = size_t(YEARS) * size_t(REGIONS) * sizeof(int);
   const size_t structArrSz = size_t(YEARS) * sizeof(AnimalPopulationForYear_s);

   //Vars to hold struct and merged subarray memory inside it.
   AnimalPopulationForYear_s * h_pop;
   int * dev_hyenas, * dev_rabbits, * h_hyenas, * h_rabbits, arrEl;

   //Alloc. memory.
   h_pop = (AnimalPopulationForYear_s *) malloc(structArrSz);
   h_rabbits = (int *) malloc(fullArrSz);
   h_hyenas = (int *) malloc(fullArrSz);
   gpuErrchk(cudaMalloc((void **) &dev_pop,structArrSz));
   gpuErrchk(cudaMalloc((void **) &dev_rabbits,fullArrSz));
   gpuErrchk(cudaMalloc((void **) &dev_hyenas,fullArrSz));

   //Offset ptrs.
   for (int i = 0; i < YEARS; i++)
   {
      h_pop[i].rabbits = dev_rabbits+i*REGIONS;
      h_pop[i].hyenas = dev_hyenas+i*REGIONS;
   }

   //Copy host struct with dev. pointers to device.
   gpuErrchk
      (cudaMemcpy(dev_pop,h_pop, structArrSz, cudaMemcpyHostToDevice));

   //Call kernel
   for(int i=1; i < YEARS+1; i++) RunSim<<<REGIONS/128+1,128>>>(dev_pop,i);

   //Make sure nothing went wrong.
   gpuErrchk(cudaPeekAtLastError());
   gpuErrchk(cudaDeviceSynchronize());

   gpuErrchk(cudaMemcpy(h_pop,dev_pop,structArrSz, cudaMemcpyDeviceToHost));
   gpuErrchk
      (cudaMemcpy(h_rabbits, dev_rabbits,fullArrSz, cudaMemcpyDeviceToHost));
   gpuErrchk(cudaMemcpy(h_hyenas,dev_hyenas,fullArrSz, cudaMemcpyDeviceToHost));

   for(int i=0; i < YEARS; i++)
   {
      h_pop[i].rabbits = h_rabbits + i*REGIONS;
      h_pop[i].hyenas = h_hyenas + i*REGIONS;
   }

   for(int i=1; i < YEARS+1; i++)
   {
      arrEl = i-1;
      printf("\nYear %i\n=============\n\n", i);      
      printf("Rabbits\n-------------\n");
      for (int j=0; j < REGIONS; j++)
     printf("Region: %i  Pop: %i\n", j, h_pop[arrEl].rabbits[j]);;      
      printf("Hyenas\n-------------\n");
      for (int j=0; j < REGIONS; j++)
     printf("Region: %i  Pop: %i\n", j, h_pop[arrEl].hyenas[j]);
   }

   //Free on device and host
   cudaFree(dev_pop);
   cudaFree(dev_rabbits);
   cudaFree(dev_hyenas);

   free(h_pop);
   free(h_rabbits);
   free(h_hyenas);

   return 0;
}

[最后]正确的结果:

  

1年级   =============

中的兔子   -------------
地区:0流行音乐:1
地区:1流行音乐:2
地区:2流行音乐:3
地区:3流行音乐:4
地区:4流行音乐:5
  地区:5流行音乐:6
地区:6流行音乐:7
地区:7流行音乐:8
  地区:8流行音乐:9
地区:9流行音乐:10
地区:10流行音乐:   11
地区:11流行音乐:12
地区:12流行音乐:13
地区:13   流行音乐:14
地区:14流行音乐:15
地区:15流行音乐:16
地区:   16流行音乐:17
地区:17流行音乐:18
地区:18流行音乐:19
  地区:19流行音乐:20
鬣狗   -------------
地区:0流行音乐:2
地区:1流行音乐:2
地区:2流行音乐:2
地区:3流行音乐:2
地区:4流行音乐:2
  地区:5流行音乐:2
地区:6流行音乐:2
地区:7流行音乐:2
  地区:8流行音乐:2
地区:9流行音乐:2
地区:10流行音乐:2
  地区:11流行音乐:2
地区:12流行音乐:2
地区:13流行音乐:   2
地区:14流行音乐:2
地区:15流行音乐:2
地区:16   流行音乐:2
地区:17流行音乐:2
地区:18流行音乐:2
地区:19   流行:2年2年   =============

中的兔子   -------------
地区:0流行音乐:4
地区:1流行音乐:8
地区:2流行音乐:12
地区:3流行音乐:16
地区:4流行音乐:   20
地区:5流行音乐:24
地区:6流行音乐:28
地区:7   流行音乐:32

地区:8流行音乐:36

地区:9流行音乐:40
地区:   10流行音乐:44

地区:11流行音乐:48

地区:12流行音乐:52
  地区:13流行音乐:56

地区:14流行音乐:60
地区:15流行音乐:   64
地区:16流行音乐:68
地区:17流行音乐:72
地区:18   流行音乐:76
地区:19流行音乐:80
鬣狗   -------------
地区:0流行音乐:2
地区:1流行音乐:2
地区:2流行音乐:2
地区:3流行音乐:2
地区:4流行音乐:2
  地区:5流行音乐:2
地区:6流行音乐:2
地区:7流行音乐:3
  地区:8流行音乐:3
地区:9流行音乐:4
地区:10流行音乐:4
  地区:11流行音乐:4
地区:12流行音乐:5
地区:13流行音乐:   5
地区:14流行音乐:6
地区:15流行音乐:6
地区:16   流行音乐:6
地区:17流行音乐:7
地区:18流行音乐:7
地区:19       ...

注3:
以下talonmies在我的代码中清除了多个数组索引不一致等。

对于AoSoA中的前两个点,结果看起来是正确的SoA(参见新输出)。出于某种原因,来自第三点(year 3)的结果现在给出了错误的结果,尽管GPU没有错误代码。我将查看指针(h_pop[year-1].rabbitsh_pop[year-1].hyenas)并查看是否有任何显示内容。

我对其他任何尝试AoSoA的人的唯一建议 - 对索引和内存分配非常小心。当然,这是一个很好的建议,但是在AoSoA等复杂的多层次数据容器中,所有内存都会飞来飞去,如果你的草率增加,则出现错误的倾向。感谢您的耐心等待,talonmies

注2:
所以按照talonmies的建议,我修复了我的循环#ing,包裹了我的cuda调用w。错误检查并通过重复使用cudaMemcpy / dev_rabbits来压缩我的dev_hyenas来电。当我考虑[djmj] [4]关于套管的抱怨时,我也认为NVIDIA确实小写了常量中的第一个字母,所以[djmj] [ 4]是对的,从某种意义上说,无论我的个人偏好/经验如何,我都应该像我那样设计代码以保持一致性。

通常也会清理代码,就像我写的那样睡不着觉而且有点吓坏了@它是多么草率。

现在我遇到了一个新问题,但是......我的程序挂起@ cudaMemcpy,并且没有返回(因此talonmies&#39; s方便的包装没有抓到任何东西)。我不太清楚为什么会这样......我已经编译了几个程序,包括设备上更大/更长时间运行的程序,它们都可以正常工作。

此时我感到困惑。如果它仍然不起作用,可能会在早上发布一些内容。

注1
第一个答案似乎真的忽略了这一点。这只是一个玩具代码,它并不代表真正的程序。它的唯一目的是尝试设置内存,为它写一些垃圾并将其读回来,以验证AoSoA是否正常工作。

所以在共享内存等方面向我发表评论并不会有效。这不是这个主题的重点。当然,如果这是一个真正的代码,我将在我的内核中消除分支,使用共享内存,对齐我的数据,使用warp级别求和等。我已经完成了以前代码中的所有操作并使其正常工作

这段代码是玩具,概念证明代码,仅此而已,旨在让AoSoA工作。这是它唯一的目的,它不是真正的代码。这是一个概念证明。

至于var名称的大小写,我在两个不同的地方工作,在编码标准中使用了完全套用的var名称(他们使用了标签,我在结构/ typedef上做_s),所以它有点卡住了。对不起,你不喜欢它。至于缩进,我稍后会尝试解决这个问题...... Windows和Linux的播放效果不佳。

还有一点需要注意,如果您因设备指针偏移而感到困惑,请在此处查看Anycom的答案:
Pointers in structs passed to CUDA

我编写了以下代码来测试CUDA中包含数组的结构数组....

编辑:修正代码 - 在meh之后和hi之前挂起,大概是cudaMemcpy ...不确定原因!

...知道这里发生了什么,以及如何解决它?

注意: 我担心cudaFree可能会搞砸了,但删除它们什么也没做。 [4]:

1 个答案:

答案 0 :(得分:4)

这段代码有很多错误,但是你要问的“乱码”结果的基本原因是你正在看未初始化的内存。 dev_Pop[0].Rabbits永远不会设置为设备内存中的任何内容,因此您不应对其内容“乱码”感到惊讶。问题的根本原因是:

for(int i=1; i < YEARS+1; i++)
    RunSim<<<REGIONS/128+1,128>>>(dev_Pop,i);

这里从year=1开始,意味着year=0永远不会设置为任何内容,year=YEARS是设备内存中保证的缓冲区溢出。

稍后在复制代码中,您可以在每次迭代时执行此操作:

cudaFree(h_Pop[i].Rabbits);
cudaFree(h_Pop[i].Hyenas);

但是你从来没有将它们首先复制到一起,所以复制操作也可能会失败。如果没有编译和运行代码,它将如何失败很难说,但我会猜测 CUDA运行时将在第一次调用时完全释放dev_Rabbitsdev_Hyenas。这应该使循环中的后续cudaMemcpy调用失败。无论精确的机制如何,如果您的复制回路成功将所有数据都恢复到主机,我会非常惊讶。一个更加理智的实现将是您用于构建设备内存映像的代码的工作原理,如:

const size_t dsize = size_t(YEARS) * size_t(REGIONS) * sizeof(int);
int * Rabbits = (int *) malloc(dsize);
int * Hyenas = (int *) malloc(dsize);
cudaMemcpy(Rabbits, dev_Rabbits, dsize, cudaMemcpyDeviceToHost);
cudaMemcpy(Hyenas, dev_Hyenas, dsize, cudaMemcpyDeviceToHost);

for(int i=0; i < YEARS; i++)
{
    h_Pop[i].Rabbits = Rabbits + i*REGIONS;
    h_Pop[i].Hyenas = Hyenas + i*REGIONS;
}

这样做可以消除PCI-e总线上的大量冗余设备 - >主机事务,以及循环内所有不必要的主机端malloc调用。

所以我猜测代码中发生了多个运行时故障点,但是因为你忽略了包含任何错误检查,所以事情就会无声地失败而你却没有注意到。要解决此问题,请在代码中添加以下内容:

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);
    }       
}

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }

然后使用gpuErrchk来测试每个 API调用的返回状态,例如:

gpuErrchk(cudaMalloc((void **) &dev_Pop,YEARS*sizeof(AnimalPopulationForYear_s)));

对于你的内核发布,我建议这样做:

RunSim<<<REGIONS/128+1,128>>>(dev_Pop,i);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());

这将捕获导致启动失败的非法参数和资源耗尽,以及导致内核中止的任何执行错误。有了这个错误检查,我怀疑在代码实际运行完成之前你会发现很多漏洞需要修复....


修改

似乎你已经决定为修改后的代码创造新的和不寻常的方法 - 包括打破你在原始代码中正确的东西,似乎是你的问题的主题 - 设备的构造结构的内存数组。

这是您的第二个代码的略微简化和工作版本。我所能建议的只是研究它,直到你知道它为什么在当前版本失败的情况下工作。

#include <cstdio>
#include <cstdlib>

#define REGIONS 20
#define YEARS 5
#define POPMIN 2
#define POPMAX 100000

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);
   }
}

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }

struct Population_s
{
   int * rabbits;
   int * hyenas;
};

__global__ void RunSim(Population_s * dev_pop, int year)
{
   int idx = blockIdx.x*blockDim.x+threadIdx.x;

   if (idx < REGIONS) {
      int rabbits, hyenas;

      rabbits = min(POPMAX, idx * year * year); 
      hyenas = max(POPMIN, rabbits / 10);

      dev_pop[year-1].rabbits[idx] = rabbits;
      dev_pop[year-1].hyenas[idx] = hyenas;
   }
}

int main()
{
   const size_t subArrSz = size_t(REGIONS) * sizeof(int);
   const size_t fullArrSz = size_t(YEARS) * subArrSz;
   const size_t structArrSz = size_t(YEARS) * sizeof(Population_s);

   Population_s * h_pop = (Population_s *) malloc(structArrSz);
   int * h_rabbits = (int *) malloc(fullArrSz);
   int * h_hyenas = (int *) malloc(fullArrSz);

   Population_s * dev_pop;
   int * dev_hyenas, * dev_rabbits;

   gpuErrchk(cudaMalloc((void **) &dev_pop,structArrSz));
   gpuErrchk(cudaMalloc((void **) &dev_hyenas,fullArrSz));
   gpuErrchk(cudaMalloc((void **) &dev_rabbits,fullArrSz));

   gpuErrchk(cudaMemset(dev_rabbits, 1, fullArrSz));
   gpuErrchk(cudaMemset(dev_hyenas, 1, fullArrSz));

   for (int i = 0; i < YEARS; i++)
   {
      h_pop[i].rabbits = dev_rabbits + i*REGIONS;
      h_pop[i].hyenas = dev_hyenas + i*REGIONS;
   }

   gpuErrchk
      (cudaMemcpy(dev_pop,h_pop, structArrSz, cudaMemcpyHostToDevice));

   for(int i = 1; i < (YEARS+1); i++) {
       RunSim<<<REGIONS/128+1,128>>>(dev_pop,i);
       gpuErrchk(cudaPeekAtLastError());
       gpuErrchk(cudaDeviceSynchronize());
   }

   gpuErrchk(cudaMemcpy(h_rabbits, dev_rabbits, fullArrSz, cudaMemcpyDeviceToHost));
   gpuErrchk(cudaMemcpy(h_hyenas, dev_hyenas, fullArrSz, cudaMemcpyDeviceToHost));

   for(int i=0; i < YEARS; i++)
   {
      h_pop[i].rabbits = h_rabbits + i*REGIONS;
      h_pop[i].hyenas = h_hyenas + i*REGIONS;
   }

   for(int i=0; i < YEARS; i++)
   {
      printf("\n=============\n");   
      printf("Year %i\n=============\n\n", i+1);   
      printf("Rabbits\n-------------\n", i);
      for (int j=0; j < REGIONS; j++)
         printf("Region: %i  Pop: %i\n", j, h_pop[i].rabbits[j]);;      
      printf("\nHyenas\n-------------\n", i);
      for (int j=0; j < REGIONS; j++)
         printf("Region: %i  Pop: %i\n", j, h_pop[i].hyenas[j]);
   }

   cudaFree(dev_pop);
   cudaFree(dev_rabbits);
   cudaFree(dev_hyenas);

   free(h_pop);
   free(h_rabbits);
   free(h_hyenas);

   return 0;
}

作为最后的提示 - 在您自己的代码中使用SDK cutil库中的任何内容,这不是它的目的。它不是CUDA的官方部分,没有文档,不被认为是生产就绪,并且不保证在任何给定版本的CUDA SDK中都可以工作,相同或甚至存在。