在CUDA中计算嵌套循环的索引

时间:2018-08-21 18:30:57

标签: cuda pycuda

我正在尝试学习CUDA,我对计算线程索引有些困惑。假设我正在尝试并行处理此循环:

...
for(int x = 0; x < DIM_x; x++){
    for(int y = 0; y < DIM_y; y++){
        for(int dx = 0; dx < psize; dx++){
            array[y*DIM_x + x + dx] += 1;
        }
    }
}

在PyCUDA中,我设置:

block = (8, 8, 8)
grid = (96, 96, 16)

我所看到的用于并行化循环的大多数示例都是这样计算线程索引的:

int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
int dx = blockIdx.z * blockDim.z + threadIdx.z;

if (x >= DIM_x || y >= DIM_y || dx >= psize)
    return;

atomicAdd(&array[y*DIM_x + x + dx], 1)

DIM_x = 580,DIM_y = 550,psize = 50

但是,如果我打印x,我看到创建了具有相同线程ID的多个线程,并且最终结果是错误的。

相反,如果我使用此(3D块的3D网格):

int blockId = blockIdx.x + blockIdx.y * gridDim.x
              + gridDim.x * gridDim.y * blockIdx.z;

int x = blockId * (blockDim.x * blockDim.y * blockDim.z)
        + (threadIdx.z * (blockDim.x * blockDim.y))
        + (threadIdx.y * blockDim.x) + threadIdx.x;

它解决了x的多个相同线程ID问题,但是我不确定如何并行化y和dx。

如果有人可以帮助我了解我要去哪里,并向我展示并行化循环的正确方法,我将不胜感激。

2 个答案:

答案 0 :(得分:1)

  

但是,如果我打印x,则会看到多个线程具有相同的   创建线程ID,最终结果是错误的。

在多维网格中看到具有相同x线程ID的多个线程是很正常的,因为在宿主代码中观察到具有相同x值的循环的多次迭代也很正常。如果结果错误,则与您显示的任何代码无关,即:

#include <vector>
#include <thrust/device_vector.h>
#include <thrust/copy.h>
#include <assert.h>

void host(int* array, int DIM_x, int DIM_y, int psize)
{
    for(int x = 0; x < DIM_x; x++){
        for(int y = 0; y < DIM_y; y++){
            for(int dx = 0; dx < psize; dx++){
                array[y*DIM_x + x + dx] += 1;
            }
        }
    }
}


__global__
void kernel(int* array, int DIM_x, int DIM_y, int psize)
{
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;
    int dx = blockIdx.z * blockDim.z + threadIdx.z;

    if (x >= DIM_x || y >= DIM_y || dx >= psize)
        return;

    atomicAdd(&array[y*DIM_x + x + dx], 1);
}

int main()
{
    dim3 block(8, 8, 8);
    dim3 grid(96, 96, 16);

    int DIM_x = 580, DIM_y = 550, psize = 50;

    std::vector<int> array_h(DIM_x * DIM_y * psize, 0);
    std::vector<int> array_hd(DIM_x * DIM_y * psize, 0);
    thrust::device_vector<int> array_d(DIM_x * DIM_y * psize, 0);

    kernel<<<grid, block>>>(thrust::raw_pointer_cast(array_d.data()), DIM_x, DIM_y, psize);
    host(&array_h[0], DIM_x, DIM_y, psize);

    thrust::copy(array_d.begin(), array_d.end(), array_hd.begin());
    cudaDeviceSynchronize();

    for(int i=0; i<DIM_x * DIM_y * psize; i++) {
        assert( array_h[i] == array_hd[i] ); 
    }

    return 0;
}

编译并运行时

$ nvcc -arch=sm_52 -std=c++11 -o looploop loop_the_loop.cu 
$ cuda-memcheck ./looploop 
========= CUDA-MEMCHECK
========= ERROR SUMMARY: 0 errors

没有错误,并针对您问题中的宿主代码通过了所有元素的检查。

如果得到的结果不正确,则可能是在运行内核之前初始化设备内存存在问题。否则,我看不到您显示的代码如何发出不正确的结果。

通常,像您的代码一样,执行大量的原子内存事务并不是在GPU上执行计算的最佳方法。使用非原子事务可能需要依赖其他关于问题结构的先验信息(例如图形分解或问题写模式的精确描述)。

答案 1 :(得分:0)

在具有3D块的3D网格中,线程ID为:

    unsigned long blockId = blockIdx.x 
             + blockIdx.y * gridDim.x 
             + gridDim.x * gridDim.y * blockIdx.z; 
    unsigned long threadId = blockId * (blockDim.x * blockDim.y * blockDim.z)
              + (threadIdx.z * (blockDim.x * blockDim.y))
              + (threadIdx.y * blockDim.x)
              + threadIdx.x;

不是您计算的xx只是该3D矩阵的x索引。

有一个不错的备忘单in this blog