1 核函数

先看一个cuda版本的hello world

#include <stdio.h>

__global__ void helloworld()
{
    printf("hello world\n");
}

int main()
{
    helloworld()<<<1, 1>>>();
    cudaDeviceSynchronize();
    return 0;
}

这里helloworld()<<<1, 1>>>();就是核函数,关于核函数:

1 核函数(kernel function)在CPU调用,GPU执行
2 核函数必须用__global__修饰,返回值必须是void
3 核函数有两种写法,__global__ void xxx()或者 void __global__ xxx()
4 核函数的几个限制
	4.1 核函数只能访问GPU内存
	4.2 核函数不能使用变长参数
	4.3 核函数不能使用static变量
	4.4 核函数不能使用函数指针
	4.5 核函数具有异步性

helloworld()<<<1, 1>>>();这行代码是什么意思,我们还不知道,不着急,接着往下看。

2 线程管理

当内核函数开始执行,如何组织GPU的线程就变成了最主要的问题了,我们必须明确,一个核函数只能有一个grid,一个grid可以有很多个块,每个块可以有很多的线程,这种分层的组织结构使得我们的并行过程更加自如灵活:
在这里插入图片描述
一个线程块block中的线程可以完成下述协作:

  • 同步
  • 共享内存

不同块内线程不能相互影响!他们是物理隔离的!

3 线程索引计算方式

3.1 内建变量gridDim / blockDim / blockIdx / threadIdx

内建变量只在核函数有限,且无需定义。
threadIdx是一个uint3类型,表示一个线程的索引。
blockIdx是一个uint3类型,表示一个线程块的索引,一个线程块中通常有多个线程。
blockDim是一个dim3类型,表示线程块的大小。
gridDim是一个dim3类型,表示网格的大小,一个网格中通常有多个线程块。

gridDim和blockDim

gridDim和blockDim是dim3类型(基于uint3定义的数据结构)的变量,都包含三个字段x,y,z,这两个内建变量三个字段的值分别等于初始化时grid_size和block_size的值。如:

dim3 grid_size(2,1,1);
dim3 block_size(3,2,1);
kernel_xxx<<<<gird_size, block_size>>>();

/*
gridDim.x=2,  gridDim.y=1,  gridDim.z=1
blockDim.x=3, blockDim.y=2, blockDim.z=1
*/

Note: gridDim和blockDim没有指定的维度默认为1

blockIdx和threadIdx

blockIdx和threadIdx是类型为uint3的变量,该类型是一个结构体,包含x,y,z三个字段,其取值范围如下:
blockIdx.x 取值范围[0, gridDim.x -1]
blockIdx.y 取值范围[0, gridDim.y -1]
blockIdx.z 取值范围[0, gridDim.y -1]
threadIdx.x 取值范围[0, blockDim.x -1]
threadIdx.y 取值范围[0, blockDim.y -1]
threadIdx.z 取值范围[0, blockDim.z -1]

3.2 线程索引计算方式

一个Grid可以包含多个Blocks,Blocks的组织方式可以是一维的,二维或者三维的。block包含多个Threads,这些Threads的组织方式也可以是一维,二维或者三维的。所以共有九种方式。
CUDA中每一个线程都有一个唯一的标识ID—ThreadIdx,这个ID随着Grid和Block的划分方式的不同而变化,这里给出Grid和Block不同划分方式下线程索引ID的计算公式。

在这里插入图片描述
第一种:一维grid,一维block

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

第二种:一维grid,二维block

int threadId = blockIdx.x * blockDim.x * blockDim.y+ threadIdx.y * blockDim.x + threadIdx.x;  

第三种:一维grid,三维block

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

第四种:二维grid,一维block

int blockId = blockIdx.y * gridDim.x + blockIdx.x;  
    int threadId = blockId * blockDim.x + threadIdx.x; 

第五种:二维grid,二维block

int blockId = blockIdx.x + blockIdx.y * gridDim.x;  
    int threadId = blockId * (blockDim.x * blockDim.y)  

第六种:二维grid,三维block

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

第七种:三维grid,一维block

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

第八种:三维grid,二维block

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

第九种:三维grid,三维block

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

3.3 网格大小限制

gridDim.x  最大值 2^31-1
gridDim.y  最大值 2^16-1
gridDim.z  最大值 2^16-1

blockDim.x  最大值 1024
blockDim.y  最大值 1024
blockDim.z  最大值 64
Logo

欢迎来到FlagOS开发社区,这里是一个汇聚了AI开发者、数据科学家、机器学习爱好者以及业界专家的活力平台。我们致力于成为业内领先的Triton技术交流与应用分享的殿堂,为推动人工智能技术的普及与深化应用贡献力量。

更多推荐