CUDA中嵌套循环的索引计算

CUDA中嵌套循环的索引计算,cuda,pycuda,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; } } } 我看到的大多数并行化循环示例都是这样计算线

我正在努力学习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;
        }
    }
}
我看到的大多数并行化循环示例都是这样计算线程索引的:

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\u x
=580,
DIM\u y
=550,
psize
=50

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

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

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


如果有人能帮助我理解哪里出了问题,并告诉我并行循环的正确方法,我将不胜感激。

在带有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;
不是您计算的
x
x
只是该3D矩阵的
x
索引

有一张很好的备忘单

但是,如果我打印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;
}
不会发出任何错误,并且通过了对问题中宿主代码的所有元素的检查

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


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

“当我打印x时,我看到多个线程具有相同的线程Id”——这是一个网格,你怎么能想象没有具有相同x索引的线程?@talonmies:我理解这一点,所以我对第一个示例如何用于并行循环感到困惑。这不是不对吗?还是我没有正确设置块/网格?我不明白你在这里想问什么。如果您在问题中编辑一个,解释您希望代码执行的操作,并显示问题中存在您暗示的错误,可能会更容易。否则很难给您提供答案。@AnderBiguri:这就是为什么内核代码中有一个原子外接程序。@Talonmes也许我猜OP想要什么太多了,我理解为什么需要它。谢谢您的回答!我真正想知道的是,给定一个由3D块组成的3D网格,并行嵌套for循环的正确方法是什么。@Kransa如果你这样做的话,你会有一些缓慢,因为几个线程在同一个位置写入,但在其他方面似乎不错。您没有得到正确的结果吗?没有,我得到的不是将1添加到特定位置,而是900@Kransa正如Talonmes所指出的,您所展示的代码在C和CUDA中的作用是相同的,因此您正确地将翻译编写到GPU。
    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;
#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