CUDA学习笔记(LESSON1/2)——架构、通信模式与GPU硬件

最近在看视频拼接的代码,师兄说要用CUDA加速,于是开始学习CUDA编程,课程链接:UdacityCS344


CUDA系列笔记

CUDA学习笔记(LESSON1/2)——架构、通信模式与GPU硬件

CUDA学习笔记(LESSON3)——GPU基本算法(Part I)

CUDA学习笔记(LESSON4)——GPU基本算法(Part II)

CUDA学习笔记(LESSON5)——GPU优化

CUDA学习笔记(LESSON7)——常用优化策略&动态并行化


CUDA线程架构

CUDA架构由Grid、Block、Thread组成。

threadIdx代表一个block内线程索引值,在不同线程内该索引值都不同,最多存在三维,用.x、.y、.z表示

blockDim代表一个block内的线程总数,最多存在三维

blockIdx代表一个grid内块的索引值

gridDim代表一个grid内的块总数

 

CUDA程序架构

下图展示了CUDA程序的架构,我们可以同时对CPU与GPU进行操纵,我们把CPU叫做Host,GPU叫做Device,我们在GPU上运行相同的串行程序,我们把这个程序叫做kernel。另外CPU与GPU不共用内存。

 

 

通信模式

通信模式指的是kernel(内核程序)中输入与输出之间的关系,该关系分为四种:map(映射)、gather(收集)、scatter(散射)、stencil(模板)、transpose(转置)

map

map描述的是输出与输入之间一对一的映射关系

gather

gather描述的是一个输出由多个输入进行运算得到。我们把相邻thread分配给相邻输出元素,由它们决定每个输出元素由哪些输入元素计算得到。

 

scatter

scatter描述的是一个输入参与多个输出的运算。我们把相邻thread分配给相邻输入元素,由它们决定每个输入元素分配给哪些输出元素。

 

stencil

stencil是一种模板,代表输出由相邻元素以一种固定的方式合成。gather就是一种stencil。需要注意的是stencil与gather不同的是stencil一定是每一个输出都由相同的输入模式确定,而gather则不一定要求输入以相同的模式合成输出。例如如下代码的下面一行:

由于有判断条件,因此不是所有输出都执行相同的操作,因此这种情况属于gather,而不是stencil

 

transpose

 

转置操作实际上就是输入序列的重排

 

GPU硬件与内存模型

GPU功能

当我们写完程序以后,GPU的工作就是把一个kernel内不同block分配给不同的SM(流处理器),只有当SM执行完其内所有block中的所有线程后,GPU才会给它分配新的block。用户无法操控哪个block分配给哪个SM,也不能操控哪个block先执行。但是GPU可以保证一个block内的所有thread是同时运行的,也可以确定当下一个kernel启动时,前一个kernel内的所有线程一定运行完毕。

 

线程同步

在对global memory操作的时候我们经常需要考虑的一个问题就是线程同步问题。当多个线程需要对同一个内存进行读写的时候,我们就需要用到synchronize(同步)的技术,其中一个重要的概念那就是barrier。barrier指程序中的一个节点,当一个block内所有thread到达这的时候就会暂停运行,直到所有thread都到达这一点,各个线程才会继续运行。而这个过程也就是所谓的线程同步。而我们之前谈到的不同kernel之间实际上存在隐式barrier来保证一个kernel所有thread运行完成以后才开启下一个kernel

 

内存模型

 

GPU的内存分为三个部分,local memory、shared memory与global memory。local memory相当于局部变量,只在本线程内可以访问,shared memory只在一个block内可以访问,而global memory相当于全局变量,在所有线程中都可以对其访问。而相应带来的结果是访问的速度有所不同,最快的是local memory,其次是shared memory,最慢是global memory,我们在做并行运算的时候希望能够让花在计算上的时间达到最大,而从内存读写的时间降到最低,因此,我们经常会把需要经常读写的数据从global memory中转移到shared memory中,这样能够大大的提高程序的效率。

 

合并存取模式

合并存取模式(coalesce access pattern)也是一种减少读写时间的方法,其主要用于从global memory读取数据的时候使用。

这个概念在图中已经描述的很清楚了,当thread从连续的内存中读写数据的时候效率是最高的,因为从global memory中读写数据的时候是以一个最小的单位(chunk)来进行操作的,如果读写位置越分散,那么需要的chunk越多,效率也就越低。

 

原子内存操作

原子内存操作(atomic memory operation)是为了解决多个线程对同一块内存区域访问时产生的冲突问题。这个操作跟线程同步的区别是:

线程同步是解决多个线程在不同时间段内对同一块内存进行读写操作产生的冲突。这很容易理解,我们先对一块内存进行读操作,之后进行线程同步以保证所有读操作都进行完毕,再进行写操作,这样就避免了写操作覆盖了原来的内容的问题。

而原子内存操作是解决在同一时间点多对线程需要访问同一块内存而进行的操作,具体方法就是将并行的线程串行化,使得同一时间点只有一个线程能对同一块内存进行访问。

而原子内存操作的局限就是它会比普通的读写操作要慢,但是很多时候为了解决冲突我们不得不采用这种操作。

原子的意思是将read-modify-write三个操作整合成一个操作,称其为原子操作,在原子操作的进行过程中其他线程不能对这块内存进行修改

 

线程发散

线程发散(thread divergence)指的是在kernel中存在条件语句或循环语句,使得不同线程经过的路径不同,我们把这种情况叫做发散。它的缺点就是效率降低了,因为运行快的线程不得不停下来等待运行慢的线程以保证程序的收敛。因此我们应尽量避免程序的发散

条件语句的发散

循环语句的发散

不同线程循环次数不同导致效率降低

 

 

 

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值