Udacity cs344-Introduction to Parallel Programming笔记(超详细,CUDA,并行,GPU)---Unit 2
1.通信
2.通信的不同类型以及并行计算中,不同的通信模型(about how to map tasks(which are threads in cuda) and meory together)
通信的模式叫做映射(communication pattern is called map)
有在黄格子里的每个元素,你将对每个元素进行同样的函数或者计算任务
这是一个线程的简单映射
有些不是这么简单的
比如依次取三个数的平均值或者用一个像素周围像素的平均值来表示此像素的这种模糊计算
除了这种聚集操作,还有分散操作
还有模板操作(stencil):用给定的模板对周围的像素进行着色
模板有几个小块,每个小块就会被重复读取多少次
另一种操作:转置(transpose )【数组运算,矩阵,图像操作】按顺序读不按顺序写
这种操作不仅适用于【数组运算,矩阵,图像操作】
也适用于各种数据结构(本质是任务重新排序内存中的数据元素)
比如
测试
又引出两种:归约和扫描
3.线程如何一齐有效的访问内存?
先讨论一个子问题:如何利用数据重用(how to exploit the data reuse)——有很多线程经常同时访问相同数据,我们如何利用这一点来减少在内存上花费的总时间?
4.线程如何通过 共享内存 来交流部分结果?(communicate partial results by sharing memory)
一个真实的问题是:线程是共享内存的,在相同的内存上进行读写,如何有效的防止内存访问冲突
5.为了解决这两个问题,要更深入的了解GPU的硬件
a.总结之前学过的:programmer's view of the GPU
每个线程走的路径可能都不一样
关键点事它们以线程块的形式出现
又有一些问题:
还需要学一些GPU硬件知识
非常重要!
这些,程序员都不能保证
CUDA可以保证的
5.内存模型
GPU上:
a.每个线程自己可以访问的——local memory
b.一个线程块中的线程都可以访问的——shared memory
c.一个SM中所有线程都可以访问的——global memory
CPU:有自己的内存(host memory),而且还会启动核函数
6.同步性
避免在a线程想读时候,b线程还没改完(本应读取到的是b线程改完后的结果),同步操作可以避免这样的问题发生
同步性的最简单形式是屏障(barrier):设置一个屏障,等待所有线程都到达,再一起跑
什么时候需要用到屏障呢?
比如:把后一个数组元素赋值给前一个
共需要三次屏障
(sync threads是专门部署同步的:比如屏障barrier)
第一次:保证线程对数组中所有元素都赋值完成
第二次:保证我把【后值】取出来的时候,别的线程别动,别往我这里写入??
第三次:保证所有的【后赋前】的操作都完成
内核之间会隐式调用一个屏障
比如调用a内核,接着调用b内核,那么只有在a核完成后,b核才允许调用
推出
CUDA的核心
7.小测验
(必须要有屏障的原因是:没法能保证统一块内存上 读取操作 会在写入操作之前完成)怎么保证呢?用临时变量
8.writing efficient programs
先谈高层次策略(不谈底层具体实现和优化):这些是在你编写程序时必须要牢记的
a.最大化计算能力
GPU算力很强,但是访问内存会浪费时间,导致算力使用率下降;
对应解决办法是:
1)最大化每个线程的计算量(工作量)
2)最小化每个线程访问内存的时间!(因为线程除了在访问内存就是在计算)
9.下面讨论如何减少访问内存的时间
一、
10.本地内存(local memory)
a.f就是(在__global__大括号里面建的)
b.in也是(__global__的参数):每个线程又有a copy of a parameter called in
11.使用全局内存
传递给kernel的参数,参数依旧是存在local memory的,但是参数本身是一个指针,这个指针指向了global memory
指针是指向global memory的
一旦kernel得到了这个指针,他就可以操纵指针指向的global memory中存的内容、
也就是说:如果你想要操作全局内存,必须给kernel传递这个内存的指针过来
怎么在CPU上分配?
*d_arr是指向我在设备上分配的global memory的
下面开始在global memory上分配内存(本质是把一个指针变量传递给另一个指针变量)
后面那个参数意思是(是个大小——字节数):开辟128个float的空间,并且返回型是指针
前面那个参数:是一个指针
cudaMalloc完成的功能:将后面的指针填充给前面的指针
cudaMalloc就是用来在global memory上分配房间的
接着来初始化(目标地址指针,来源地址指针,字节大小,copy方向)给global 的 房间里填上值
在kernel外面就已经分配好的,非host memory 的内存就是global
12.怎么使用共享内存
怎么声明shared memory中的变量?
最重要的一点,已知
把全局变量中的array放入共享变量中
在所有【写】操作之后都要注意是否需要同步
注意到,计算所有值的和是在共享内存上进行的,因为:共享内存上计算快!每个线程要访问数组中的一堆元素,
而判断数组中元素的值是否大于平均值是在全局内存上进行的,因为:当kernel完成这个改动时,host是看得见的!也会被其他线程块中的线程看到
最后一个语句完全没有用,因为随着线程块消失,共享内存也消失了,通常会被编译器过滤掉
CPU调用完全一样
二、
13.合并全局内存访问
14.讨论一个相关问题
例子
解决方案
a.设置屏障:麻烦
b.原子操作
具体实现
限制
a.
b.
c.
15.总结第一个策略,引出第二个
11.使用全局内存