CUDA

一、核函数

  1. 核函数在GPU上并行执行

  2. 注意

    • 限定词__global__修饰
    • 返回值必须是void
  3. 形式

    1. 1
      2
      3
      4
      __global__ void kernel_function(argument arg)
      {
      printf("hello world\n");
      }

    2. 1
      2
      3
      4
      void __global__ kernel_function(argument arg)
      {
      printf("hello world\n");
      }

  • 核函数只能返回GPU内存

  • 核函数不能使用变长参数

  • 核函数不能使用静态变量

  • 核函数不能使用函数指针

  • 核函数具有异步性,需要用cudaDeviceSynchronize()同步

CUDA程序编写流程:

1
2
3
4
5
6
7
int main(void) 
{
// 主机代码
// 核函数调用
// 主机代码
return 0;
}

注意: 核函数不支持C++的iostream

二、 CUDA线程模型

1. 线程模型结构

  1. 线程模型重要概念:
  • grid 网格
  • block 线程块,包含一组线程
  • thread 线程 最小单位
在这里插入图片描述
在这里插入图片描述
  1. 线程分块是逻辑上的划分,物理上线程不分块

  2. 配置线程:<<<grid_size, block_size>>>, 一个grid grid_size个block,一个block block_size个thread

  3. 最大允许线程块大小:1024

    最大允许网格大小:\(2^{31} - 1\) (针对唯一网格)

2. 一维线程模型

  1. 每个线程在核函数中都有一个唯一的身份标识;

  2. 每个线程的唯一标识由这两个<<<grid_size, block_size>>>确定;grid_size, block_size保存在内建变量(build-in variable),目前考虑的是一维的情况:

  • gridDim.x:该变量的数值等于执行配置中变量grid_size的值;

  • blockDim.x:该变量的数值等于执行配置中变量block_size的值。

  1. 线程索引保存成内建变量
  • blockIdx.x:改变量指定一个线程在一个网格中的线程块索引值,范围为0~gridDim.x-1;

  • threadIdx.x:该变量指定一个线程在一个线程块中的线程索引值,范围为0~blockDim.x - 1。

example:

kernel_func<<<2, 4>>>();

gridDimx.x = 2

blockDimx.x = 4

0 <= blockIdx.x <= 1

0 <= threadIdx.x <= 3

线程唯一标识:

Idx = threadIdx.x + blockIdx.x * blockDim.x

3. 多维线程模型

  1. CUDA可以组织三维的网格和线程块;

  2. blockIdx和threadIdx是类型为uint3的变量,该类型是一个结构体,具有x, y, z三个成员,三个成员都为无符号类型

  3. gridDim和blockDim是类型为dim3的变量,也是一个结构体,具有x,y,z三个成员

  4. 取值范围与一维一致

内建变量只在核函数有效,且无需定义

定义多维网格和线程块

dim3 grid_size(Gx, Gy, Gz)

dim3 block_size(Bx, By, Bz)

example:

dim3 grid_size(2, 2) // 等价于dim3 grid_size(2, 2,1)

dim3 block_size(5, 3) // 等价于dim3 block_size(5, 3,1)

多为网格和多维线程块本质是一维的,GPU物理上不分块

每个线程都有唯一标识(二维)

int tid = threadIdx.y * blockDim.x + threadIdx.x;

int bid = blockIdx.y * gridDim.x + blockIdx.x;

在这里插入图片描述
在这里插入图片描述

图中block(2, 1) bid = 1 * 3 + 2 = 5

thread(3, 2) tid = 2 * 5 + 3

每个线程都有唯一标识(三维)

int tid = threadIdx.z * blockDim.x * blockDim.y (channel) + threadIdx.y * blockDim.x (y dim) + threadIdx.x (x dim);

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

4. 网格和线程块的限制条件

网格大小:

  • gridDim.x 最大值: \(2^{31} - 1\)

  • gridDim.y 最大值: \(2^{16} - 1\)

  • gridDim.z 最大值: \(2^{16} - 1\)

线程块大小限制:

  • blockDim.x 最大值: 1024

  • blockDim.y 最大值: 1024

  • blockDim.z 最大值: 64

注意:线程块总大小最大为1024

三、线程全局索引计算方式

1. 线程全局索引

1.1 一维网格一维线程块

在这里插入图片描述
在这里插入图片描述

example:

bid = 2;

tid = 1;

id = 8 * 2 + 1 = 17

1.2 二维网格二维线程块

在这里插入图片描述
在这里插入图片描述

example:

bid = 2 * 1 + 1 = 3;

tid = 4 * 2 + 1 = 9;

id = 4 * 4 * 2 + 9 = 41

1.3 三维网格三维线程块

在这里插入图片描述
在这里插入图片描述

example:

bid = 0 * 2 * 2 + 1 * 2 + 0 = 2;

tid = 0 * 4 * 4 + 2 * 4 + 1 = 9;

id = 4 * 4 * 2 * bid + tid = 32 * 2 + 9 = 73

2. 不同组合方式列举

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、nvcc编译流程与GPU计算能力

1. nvcc编译流程

1.1 编译流程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2 PTX

PTX作用:虚拟计算架构和真实架构之间的桥梁

在这里插入图片描述
在这里插入图片描述

实际计算能力必须高于虚拟计算能力才能编译通过

2. GPU计算能力

不同cubin在不同架构不兼容

在这里插入图片描述
在这里插入图片描述

主版本号相同的不同架构可以兼容

在这里插入图片描述
在这里插入图片描述

括号中为单精度计算峰值

2070性能低于V100,但是计算能力高于V100

五、CUDA程序兼容性问题

1. 指定虚拟架构计算能力

在这里插入图片描述
在这里插入图片描述

2. 指定真是架构计算能力

在这里插入图片描述
在这里插入图片描述

3. 指定多个GPU版本编译

在这里插入图片描述
在这里插入图片描述

4. nvcc即时编译

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

缺点:可能不能完全发挥高版本架构的性能

5. nvcc编译默认计算能力

在这里插入图片描述
在这里插入图片描述

nvcc ***.cu -ptx

通过.target sm_**确认主版本号和次版本号


CUDA Programming
http://chenxindaaa.com/Infra/CUDA/infra/programming/
Author
chenxindaaa
Posted on
January 8, 2020
Licensed under