CUDA数组到数组的总和

时间:2011-02-19 23:32:24

标签: cuda parallel-processing

我有一段这样的代码:

typedef struct {
  double sX;
  double sY;
  double vX;
  double vY;
  int rX;
  int rY;
  int mass;
  int species;
  int boxnum;
} particle;

typedef struct {
  double mX;
  double mY;
  double count;
  int rotDir;
  double cX; 
  double cY; 
  int superDir;
} box;
//....
int i;
for(i=0;i<PART_COUNT;i++) {
    particles[i].boxnum = ((((int)(particles[i].sX+boxShiftX))/BOX_SIZE)%BWIDTH+BWIDTH*((((int)(particles[i].sY+boxShiftY))/BOX_SIZE)%BHEIGHT));
}
for(i=0;i<PART_COUNT;i++) {
    //sum the momenta
    boxnum = particles[i].boxnum;
    boxes[boxnum].mX += particles[i].vX*particles[i].mass;
    boxes[boxnum].mY += particles[i].vY*particles[i].mass;
    boxes[boxnum].count++;
}

现在,我想把它移植到CUDA。第一步很简单;将计算分布在一堆线程上是没有问题的。问题是第二个问题。由于任何两个粒子同样可能在同一个框中,我不确定如何对其进行分区以避免冲突。

粒子数量在10,000到10,000,000之间,盒子数量在1024到1048576之间。

想法?

2 个答案:

答案 0 :(得分:1)

您可以尝试使用atomicAdd操作来修改您的盒子数组。对全局内存的原子操作非常缓慢,但同时由于两个原因进行任何涉及共享内存的优化是不可能的:

  1. 假设粒子boxnum的属性particles[0]..particles[n]没有排序且不在任何小边界(在块大小的范围内),您无法预测哪些盒子从全局内存加载到共享内存。你首先要收集所有的方框号..
  2. 如果您尝试收集所有boxnumbers,则不能使用每个可能的boxnumber作为索引的数组,因为有太多的框可以放入共享内存。所以你必须用队列收集索引(用数组实现,指向下一个空闲槽和原子操作的指针),但是你仍然会有冲突,因为你的队列中可能会多次出现相同的boxnumber。 / LI>

    结论:atomicAdd将至少为您提供正确的行为。尝试一下并测试性能。如果您对性能不满意,请考虑是否有另一种方法可以进行从共享内存中获利的相同计算。

答案 1 :(得分:1)

作为替代方案,您可以启动块的2D网格。

blocks.x = numParticles / threadsPerBlock / repeatPerBlock。

blocks.y = numOfBoxes / 1024;

当且仅当boxnum位于1024 * blockIdx.y和1024 *(blockIdx.y + 1)之间时,每个块在共享内存中执行原子添加;

然后沿blocks.x减少

这可能会或可能不会比全局内存中的atomicAdd更快,因为数据被读取了多次。但是,如果“粒子”在分拣通道中按照boxnum排序,然后是分区传递,则可以修复此问题。

可能还有其他几种方法,但由于问题大小变化很大,您最终可能需要编写2-3种针对给定大小范围进行优化的方法。