我有一段这样的代码:
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之间。
想法?
答案 0 :(得分:1)
您可以尝试使用atomicAdd
操作来修改您的盒子数组。对全局内存的原子操作非常缓慢,但同时由于两个原因进行任何涉及共享内存的优化是不可能的:
boxnum
的属性particles[0]..particles[n]
没有排序且不在任何小边界(在块大小的范围内),您无法预测哪些盒子从全局内存加载到共享内存。你首先要收集所有的方框号.. 结论: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种针对给定大小范围进行优化的方法。