1 CUDA编程模型基础
这里先对CUDA编程模型中的一些概念及基础知识做个简单介绍。CUDA编程模型是一个异构模型,需要CPU和GPU协同工作。在CUDA中,host和device是两个重要的概念,我们用host指代CPU及其内存,而用device指代GPU及其内存。CUDA程序中既包含host程序,又包含device程序,它们分别在CPU和GPU上运行。同时,host与device之间可以进行通信,这样它们之间可以进行数据拷贝。典型的CUDA程序的执行流程如下:
- 1.分配host内存,并进行数据初始化;
- 2.分配device内存,并从host将数据拷贝到device上;
- 3.调用CUDA的核函数在device上完成指定的运算;
- 4.将device上的运算结果拷贝到host上;
- 5.释放device和host上分配的内存。
1.1 CUDA软件架构
网格(Grid)、线程块(Block)和线程(Thread)的组织关系
CUDA的软件架构由网格(Grid)、线程块(Block)和线程(Thread)组成,相当于把GPU上的计算单元分为若干(2~3)个网格,每个网格内包含若干(65535)个线程块,每个线程块包含若干(512)个线程,三者的关系如下图:
Thread,block,grid是CUDA编程上的概念,为了方便程序员软件设计,组织线程。
- thread:一个CUDA的并行程序会被以许多个threads来执行。
- block:数个threads会被群组成一个block,同一个block中的threads可以同步,也可以通过shared memory通信。
- grid:多个blocks则会再构成grid。
网格(Grid)、线程块(Block)和线程(Thread)的最大数量
CUDA中可以创建的网格数量跟GPU的计算能力有关,可创建的Grid、Block和Thread的最大数量参看以下表格:
在单一维度上,程序的执行可以由多达365535512=100661760(一亿)个线程并行执行,这对在CPU上创建并行线程来说是不可想象的。
1.3 CUDA
1.4 关键字
CPU是整个计算机的核心,它的主要工作是负责调度各种资源,包括其自身的计算资源以及GPU的计算计算资源。比如一个浮点数相乘逻辑,理论上我们可以让其在CPU上执行,也可以在GPU上执行。那这段逻辑到底是在哪个器件上执行的呢?cuda将决定权交给了程序员,我们可以在函数前增加修饰词来指定。
由于GPU实际上是异构模型,所以需要区分host和device上的代码,在CUDA中是通过函数类型限定词开区别host和device上的函数,主要的三个函数类型限定词如下:
- global:在device上执行,从host中调用(一些特定的GPU也可以从device上调用),返回类型必须是void,不支持可变参数参数,不能成为类成员函数。注意用__global__定义的kernel是异步的,这意味着host不会等待kernel执行完就执行下一步。
- device:在device上执行,单仅可以从device中调用,不可以和__global__同时用。
- host:在host上执行,仅可以从host上调用,一般省略不写,不可以和__global__同时用,但可和__device__,此时函数会在device和host都编译。
关键字 | 调用位置 | 执行位置 |
---|---|---|
host | CPU | CPU |
global | CPU | GPU |
device | GPU | GPU |
注意
- __global__描述的函数就是“被CPU调用,在GPU上运行的代码”,同时它也打通了__host__和__device__修饰的函数。
- __global__既不能和__host__关键词一起用,也不能和__device__一起用。
- __global__修饰的函数既是void返回类型,且是异步调用的。假设该函数有返回值,当函数返回时,接受返回值的变量可能已经被销毁了,所以设计其有返回值也没太多意义。