Cuda gridDim和blockDim

时间:2013-05-17 23:38:37

标签: cuda

我得到的是blockDim,但我遇到问题gridDim. Blockdim给出了块的大小,但是gridDim是什么?在互联网上,它表示gridDim.x给出了x坐标中的块数。

我怎么知道blockDim.x * gridDim.x给出了什么?

我如何知道x行中有多少gridDim.x个值?

例如,请考虑以下代码:

int tid = threadIdx.x + blockIdx.x * blockDim.x;
double temp = a[tid];
tid += blockDim.x * gridDim.x;

while (tid < count)
{
    if (a[tid] > temp)
    {
       temp = a[tid];
    }
    tid += blockDim.x * gridDim.x;
}

我知道tid以0开头。然后代码有tid+=blockDim.x * gridDim.x。此操作后现在tid是什么?

4 个答案:

答案 0 :(得分:80)

  • blockDim.x,y,z给出了一个块中的线程数 特别方向
  • gridDim.x,y,z给出网格中的块数 特别方向
  • blockDim.x * gridDim.x给出网格中的线程数(在x方向,在这种情况下)

块和网格变量可以是1,2或3维。处理1-D数据时,通常只创建1-D块和网格。

在CUDA文档中,这些变量定义为here

特别是,当x维度(gridDim.x * blockDim.x)中的总线程小于我想要处理的数组的大小时,那么通常的做法是创建循环并让线程网格在整个数组中移动。在这种情况下,在处理一个循环迭代之后,每个线程必须移动到下一个未处理的位置,由tid+=blockDim.x*gridDim.x;给出。实际上,整个线程网格跳过1-D数据数组,网格宽度一次。本主题(有时称为“网格跨越循环”)将在此blog article中进一步讨论。

您可能需要考虑在NVIDIA webinar page上提供一些CUDA介绍性网络研讨会。例如,这两个:

  • 使用CUDA C进行GPU计算 - 简介(2010)简介 使用CUDA C进行GPU计算的基础知识。概念将是 用代码示例的演练来说明。没有先前的GPU计算 需要经验
  • 使用CUDA C进行GPU计算 - 高级1(2010)第一级 优化技术,如全局内存优化,以及 处理器利用率将使用真实代码来说明概念 实例

如果你想更好地理解这些概念,那将花费2个小时。

详细介绍了网格跨越循环的一般主题here

答案 1 :(得分:45)

CUDA Programming Guide

转述
  

gridDim:此变量包含网格的尺寸。

     

blockIdx:此变量包含网格中的块索引。

     

blockDim:此变量并包含块的尺寸。

     

threadIdx:该变量包含块内的线程索引。

你似乎对CUDA的线程层次感到有点困惑;简而言之,对于内核,将有1个网格(我总是可视化为3维立方体)。它的每个元素都是一个块,这样声明为dim3 grid(10, 10, 2);的网格总块数为10 * 10 * 2。反过来,每个块都是一个三维立方体线程。

话虽如此,通常只使用块和网格的x维度,这就像你问题中的代码所做的那样。如果您正在使用1D阵列,这尤其重要。在这种情况下,您的tid+=blockDim.x * gridDim.x行实际上是网格中每个线程的唯一索引。这是因为您的blockDim.x将是每个块的大小,而您的gridDim.x将是块的总数。

因此,如果您启动带参数的内核

dim3 block_dim(128,1,1);
dim3 grid_dim(10,1,1);
kernel<<<grid_dim,block_dim>>>(...);

然后在你的内核中有threadIdx.x + blockIdx.x*blockDim.x你将有效地拥有:

threadIdx.x range from [0 ~ 128)

blockIdx.x range from [0 ~ 10)

blockDim.x equal to 128

gridDim.x equal to 10

因此,在计算threadIdx.x + blockIdx.x*blockDim.x时,您将拥有在[0, 128) + 128 * [1, 10)定义的范围内的值,这意味着您的tid值范围为{0,1,2,...,1279} 。 当您想要将线程映射到任务时,这非常有用,因为这为您内核中的所有线程提供了唯一的标识符。

但是,如果你有

int tid = threadIdx.x + blockIdx.x * blockDim.x;
tid += blockDim.x * gridDim.x;

然后你基本上会:tid = [0, 128) + 128 * [1, 10) + (128 * 10),你的tid值范围是{1280,1281,...,2559} 我不确定哪些内容是相关的,但这完全取决于您的应用程序以及如何将线程映射到数据。这种映射对于任何内核启动都是非常重要的,您就是决定如何完成它的人。当您启动内核时,您需要指定网格和块尺寸,并且您必须在内核中强制执行映射到数据。只要您没有超出硬件限制(对于现代卡,每个块最多可以有2 ^ 10个线程,每个网格最多可以有2 ^ 16 - 1个块)

答案 2 :(得分:1)

在这个源代码中,我们甚至有4个thred,内核函数可以访问所有10个数组。怎么样?

#define N 10 //(33*1024)

__global__ void add(int *c){
    int tid = threadIdx.x + blockIdx.x * gridDim.x;

    if(tid < N)
        c[tid] = 1;

    while( tid < N)
    {
        c[tid] = 1;
        tid += blockDim.x * gridDim.x;
    }
}

int main(void)
{
    int c[N];
    int *dev_c;
    cudaMalloc( (void**)&dev_c, N*sizeof(int) );

    for(int i=0; i<N; ++i)
    {
        c[i] = -1;
    }

    cudaMemcpy(dev_c, c, N*sizeof(int), cudaMemcpyHostToDevice);

    add<<< 2, 2>>>(dev_c);
    cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost );

    for(int i=0; i< N; ++i)
    {
        printf("c[%d] = %d \n" ,i, c[i] );
    }

    cudaFree( dev_c );
}

为什么我们不创建10个线程()add<<<2,5>>> or add<5,2>>> 因为我们必须创建相当少数量的线程,如果N大于10 ex)33 * 1024。

此源代码是此案例的示例。 数组是10,cuda线程是4。 如何仅通过4个线程访问所有10个数组。

在cuda详细信息中查看有关threadIdx,blockIdx,blockDim,gridDim含义的页面。

在此源代码中,

gridDim.x : 2    this means number of block of x

gridDim.y : 1    this means number of block of y

blockDim.x : 2   this means number of thread of x in a block

blockDim.y : 1   this means number of thread of y in a block

我们的线程数是4,因为2 * 2(块*线程)。

在添加内核函数时,我们可以访问0,1,2,3的线程索引

- &GT; tid = threadIdx.x + blockIdx.x * blockDim.x

①0+ 0×2 = 0

②1+ 0 * 2 = 1

③0+ 1 * 2 = 2

④1+ 1 * 2 = 3

如何访问索引4,5,6,7,8,9的其余部分。 while循环中有一个计算

tid += blockDim.x + gridDim.x in while

**第一次调用内核**

-1 loop:0 + 2 * 2 = 4

-2循环:4 + 2 * 2 = 8

-3 loop:8 + 2 * 2 = 12(但这个值为false,而out!)

**内核的第二次调用**

-1循环:1 + 2 * 2 = 5

-2循环:5 + 2 * 2 = 9

-3 loop:9 + 2 * 2 = 13(但这个值为false,而out!)

**内核的第三次调用**

-1循环:2 + 2 * 2 = 6

-2循环:6 + 2 * 2 = 10(但是这个值是假的,而在外面!)

**内核的第四次调用**

-1 loop:3 + 2 * 2 = 7

-2 loop:7 + 2 * 2 = 11(但这个值为false,而out!)

因此,所有0,1,2,3,4,5,6,7,8,9的索引都可以通过tid值访问。

参考此页面。 http://study.marearts.com/2015/03/to-process-all-arrays-by-reasonably.html 我无法上传图片,因为信誉很低。

答案 3 :(得分:0)

首先,请参见Grid of thread blocks中的图CUDA official document

通常,我们使用内核:

__global__ void kernelname(...){
    const id_x = blockDim.x * blockIdx.x + threadIdx.x;
    const id_y = blockDim.y * blockIdx.y + threadIdx.y;
    ...
}

// invoke kernel
// assume we have assigned the proper gridsize and blocksize
kernelname<<<gridsize, blocksize>>>(...)

一些变量的含义:

gridsize每个网格的块数,对应于gridDim

blocksize每个块的线程数,对应于blockDim

threadIdx.x的不同之处在于[0,blockDim.x)

blockIdx.x的不同之处在于[0,gridDim.x)

因此,当我们有threadIdx.xblockIdx.x时,让我们尝试在 x方向上计算索引。根据{{​​3}},blockIdx.x确定您是哪个块,而threadIdx.x确定给出块的位置时您是哪个线程。因此,我们有:

which_blk = blockDim.x * blockIdx.x; // which block you are
final_index_x = which_blk + threadIdx.x; // based on the given block, we can have the final location by adding the threadIdx.x

即:

final_index_x = blockDim.x * blockIdx.x + threadIdx.x;

与上面的示例代码相同。

类似地,我们可以分别在 y或z方向获得索引。

如我们所见,我们通常在代码中不使用gridDim,因为此信息是在blockIdx的范围内执行的。相反,尽管此信息作为blockDim的范围执行,但我们必须使用threadIdx。我上面一步一步显示的原因。

我希望这个答案可以解决您的困惑。