Memory 当所有内核都需要全局内存访问时,如何CUDA化代码

Memory 当所有内核都需要全局内存访问时,如何CUDA化代码,memory,parallel-processing,cuda,Memory,Parallel Processing,Cuda,没有CUDA,我的代码只是两个for循环,用于计算系统中所有坐标对之间的距离,并将这些距离排序到容器中 我的CUDA版本的问题在于,显然线程不能同时写入相同的全局内存位置(竞争条件?)。我最终为每个存储箱获取的值不正确,因为只有一个线程最终写入每个存储箱 __global__ void computePcf( double const * const atoms, double * bins, int numParticles, d

没有CUDA,我的代码只是两个for循环,用于计算系统中所有坐标对之间的距离,并将这些距离排序到容器中

我的CUDA版本的问题在于,显然线程不能同时写入相同的全局内存位置(竞争条件?)。我最终为每个存储箱获取的值不正确,因为只有一个线程最终写入每个存储箱

__global__ void computePcf(
        double const * const atoms,
        double * bins,
        int numParticles,
        double dr) {

    int i = blockDim.x * blockIdx.x + threadIdx.x;

    if (i < numParticles - 1) {
        for (int j = i + 1; j < numParticles; j++) {
            double r = distance(&atoms[3*i + 0], &atoms[3*j + 0]);

            int binNumber = floor(r/dr);

            // Problem line right here.
            // This memory address is modified by multiple threads
            bins[binNumber] += 2.0;
        }
    }
}
全局无效计算cf(
双常数*常数原子,
双*箱,
int numParticles,
双dr){
int i=blockDim.x*blockIdx.x+threadIdx.x;
如果(i
所以。。。我不知道该怎么办。我一直在谷歌上搜索和阅读有关共享内存的内容,但问题是,在我进行距离计算之前,我不知道要访问哪个内存区域


我知道这是可能的,因为一个叫做VMD的程序使用GPU来加速这个计算。任何帮助(甚至想法)都将不胜感激。我不需要优化,只需要功能。

有多少个
箱子[]
? 是否有某种原因导致
箱子[]
需要为
双箱类型?从你的代码中看不明显。您所拥有的基本上是一个操作,您可能希望了解快速并行直方图技术。可能有兴趣

有几种可能的途径来考虑你的代码:

  • 看看是否有办法重新构造算法,以使给定的一组线程(或bin计算)不会相互重叠的方式安排计算。这也许可以根据排序距离来实现

  • 使用此选项可以解决您的问题,但在执行时间方面可能会付出高昂的代价(但因为它非常简单,您可能想尝试一下。)代替此选项:

    bins[binNumber] += 2.0;
    
    大概是这样的:

    int * bins,
    ...
    atomicAdd(bins+binNumber, 2);
    
    如果
    bin
    的类型为
    double
    ,您仍然可以这样做,这只是稍微复杂一点。有关如何在
    double
    上执行
    atomicAdd
    的示例,请参阅

  • 如果
    存储箱的数量很小(可能只有几千个或更少),则可以创建几个由多个线程块更新的存储箱集,然后在处理序列结束时使用缩减操作(逐个元素将存储箱集相加)。在这种情况下,您可能需要考虑使用较少数量的线程或线程块,每个线程处理多个元素,通过在内核代码中添加一个循环,从而在每个粒子处理完成后,通过添加<代码> GRIDIMD.x*BuffDim.x/Cuff>到<代码> i>代码>变量,循环跳到下一个粒子,重复这个过程。由于每个线程或threadblock都有自己的容器本地副本,所以它可以在不影响其他线程访问的情况下执行此操作

    例如,假设我只需要1000个int类型的箱子,我可以创建1000组箱子,这只需要大约4兆字节。然后我可以给1000个线程中的每个线程它自己的bin集,然后1000个线程中的每个线程都有它自己的bin集要更新,并且不需要原子,因为它不会干扰任何其他线程。通过让每个线程在多个粒子之间循环,我仍然可以通过这种方式有效地保持机器忙碌。当所有的粒子装箱完成后,我必须将我的1000个箱子集添加到一起,可能需要一个单独的内核调用