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