Performance warp中不同地址原子操作的CUDA性能

Performance warp中不同地址原子操作的CUDA性能,performance,cuda,gpu,atomic,Performance,Cuda,Gpu,Atomic,据我所知,如果原子操作在warp中的同一内存地址位置上执行,warp的性能可能会慢32倍 但是,如果一个扭曲中线程的原子操作在32个不同的内存位置上呢?有没有任何表现上的惩罚?还是会和正常运行一样快 我的用例是,我有32个不同的位置,一条经线中的每条线都需要其中一个位置,但哪个位置取决于数据。因此,每个线程都可以使用atomicCAS来扫描所需的位置是否为空。如果不为空,则扫描下一个位置 如果幸运的话,32个线程可以将原子复制到32个不同的内存位置,在这种情况下会有任何性能损失吗 我假设在下面的

据我所知,如果原子操作在warp中的同一内存地址位置上执行,warp的性能可能会慢32倍

但是,如果一个扭曲中线程的原子操作在32个不同的内存位置上呢?有没有任何表现上的惩罚?还是会和正常运行一样快

我的用例是,我有32个不同的位置,一条经线中的每条线都需要其中一个位置,但哪个位置取决于数据。因此,每个线程都可以使用atomicCAS来扫描所需的位置是否为空。如果不为空,则扫描下一个位置

如果幸运的话,32个线程可以将原子复制到32个不同的内存位置,在这种情况下会有任何性能损失吗


我假设在下面的代码中使用了开普勒体系结构,我将向数组的元素添加一个常量值(
dev_input
)。我正在比较两个内核,一个使用
atomicAdd
,另一个使用常规加法。这是一个极端的例子,
atomicAdd
在完全不同的地址上操作,因此不需要序列化操作

#include <stdio.h>

#define BLOCK_SIZE 1024

int iDivUp(int a, int b) { return ((a % b) != 0) ? (a / b + 1) : (a / b); }

#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 regular_addition(float *dev_input, float val, int N) {

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

    if (i < N) dev_input[i] = dev_input[i] + val;
}

__global__ void atomic_operations(float *dev_input, float val, int N) {

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

    if (i < N) atomicAdd(&dev_input[i],val);
}

int main(){

    int N = 8192*32;

    float* output = (float*)malloc(N*sizeof(float));
    float* dev_input; gpuErrchk(cudaMalloc((void**)&dev_input, N*sizeof(float)));

    gpuErrchk(cudaMemset(dev_input, 0, N*sizeof(float)));

    int NumBlocks = iDivUp(N,BLOCK_SIZE);

    float time, timing1 = 0.f, timing2 = 0.f;
    cudaEvent_t start, stop;

    int niter = 32;

    for (int i=0; i<niter; i++) {

        gpuErrchk(cudaEventCreate(&start));
        gpuErrchk(cudaEventCreate(&stop));
        gpuErrchk(cudaEventRecord(start,0));

        atomic_operations<<<NumBlocks,BLOCK_SIZE>>>(dev_input,3,N);
        gpuErrchk(cudaPeekAtLastError());
        gpuErrchk(cudaDeviceSynchronize());

        gpuErrchk(cudaEventRecord(stop,0));
        gpuErrchk(cudaEventSynchronize(stop));
        gpuErrchk(cudaEventElapsedTime(&time, start, stop));

        timing1 = timing1 + time;

    }

    printf("Time for atomic operations:  %3.5f ms \n", timing1/(float)niter);

    for (int i=0; i<niter; i++) {

        gpuErrchk(cudaEventCreate(&start));
        gpuErrchk(cudaEventCreate(&stop));
        gpuErrchk(cudaEventRecord(start,0));

        regular_addition<<<NumBlocks,BLOCK_SIZE>>>(dev_input,3,N);
        gpuErrchk(cudaPeekAtLastError());
        gpuErrchk(cudaDeviceSynchronize());

        gpuErrchk(cudaEventRecord(stop,0));
        gpuErrchk(cudaEventSynchronize(stop));
        gpuErrchk(cudaEventElapsedTime(&time, start, stop));

        timing2 = timing2 + time;

    }

    printf("Time for regular addition:  %3.5f ms \n", timing2/(float)niter);

}

这是另一个极端,
atomicAdd
始终在同一地址上运行。在这种情况下,执行时间增加到
5.1ms


上述测试已在费米体系结构上进行。您可以尝试在开普勒卡上运行上述代码。

开普勒GK110为全球原子公司制作了一些。共享内存呢?我编写了一段代码,并在具有
Kepler
体系结构的设备上进行了测试。我没有回答,而是提出了一些你可以看到的问题。我的结果与你的不同。“我把它作为一个问题发了出来。@Farzad在你的帖子中,你得出结论:显然,联合无冲突原子操作的性能最好,同一地址的性能最差,这也是我的结论。为什么您声称结果不同?不同之处在于,当您更改
atomicAdd(&dev_input[i],val)时,您没有观察到性能损失到原子添加(&dev_输入[i%32],val)当我从
CoalescadatomiconglobalMem
AddressRestrictedAtomicOnGlobalMem
时,速度降低了约4倍。
if (i < N) atomicAdd(&dev_input[i],val);
if (i < N) atomicAdd(&dev_input[i%32],val);
if (i < N) atomicAdd(&dev_input[0],val);