CUDA编程(八)树状加法
来源:互联网 发布:冰川网络下最火的游戏 编辑:程序博客网 时间:2024/06/07 03:06
CUDA编程(八)
树状加法
上一篇博客我们介绍了ShareMemory和Thread同步,最后利用这些知识完成了block内部线程结果的加和,减轻了CPU的负担,结果还是比较令人满意的,但是block的加和工作是使用一个thread0单线程完成的,这点还是有待改进的。
那么这个单线程的加法部分如何解决呢?我们知道GPU上的程序只有并行才能发挥其优势,所以我们自然想到这个加法能不能并行呢?答案当然是可行的,我们可以利用树状加法的方式将加法并行,这也体现了我们之前提到的,一个优秀的CUDA程序是需要一个优秀的算法为基础的。
树状加法
我们传统的加法 a + b + c + d ,只能在一个线程上进行,但是我们也很容易想到,如果把加法分成多步执行,比如先算 a+b,c+d,再把他们的结果相加,通过这样的方式我们就可以把任务分开,也就是可以并行了,这就是树状加法:
通过这种方式我们就可以把256个数的加法进行并行了。
树状加法的实现
上图是树状加法的一个示意图,示意图中第一排每一个格子就是一个线程的结果,保存在shared[],暂且把shared[0]简写为 sh0,我们可以清楚的看到计算的过程:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
其实树状加法可以写成一个很简单的while循环:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
下面我们就来看看这个while循环:
注意& 按位“与”,只有1&1 = 1
tid=0时,mask = 1,0&1=0,所以shared[0] = sh0 + sh1,完成第一步的前两个相加。
tid=1时,mask = 1,1&1=1,不作运算。
tid=2时,mask = 1,10&01 = 00,所以shared[2] = sh2 + sh3
tid=3时,mask = 1,11&01 = 01,不作运算。
…
可以看出来这是第一层的计算
同步之后第二层:
offset=1+1=2,mask=2+1=3;
tid=0时,mask = 3,0&11=0,所以shared[0] = sh0 + sh2,完成第二步的前两个相加。
tid=1时,mask = 3,1&11=1,不作运算。
tid=2时,mask = 3,10&11 = 10,不作运算。
tid=3时,mask = 3,11&11 = 01,不作运算。
tid=4时,mask = 3,100&011 = 000,所以shared[4] = sh4 + sh6
后面都以此类推,直到offset 大于等于线程数就跳出了
最终的结果就在shared[0]内,所以下一步用线程0把结果保存就OK了:
- 1
- 1
所以比起上一版的程序,我们只用改动核函数里面的加和部分就OK了,下面是改好的核函数:
核函数:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
运行结果:
我们看到比起上一次没用树状加法的144185个周期,这次只用了133738个周期,总的来说这个结果还是非常不错的,甚至和完全不在GPU上加和的程序速度差不多,这是因为,在完全不在 GPU 上进行加总的版本,写入到 global memory 的数据数量很大(8192 个数字),这对效率也会有影响。所以,这一版程序不但在 CPU 上的运算需求降低,在 GPU 上也能跑的更快~
总结:
这篇博客我们主要介绍了怎么去把加法进行并行,利用树状加法,最终并行了之前效率比较差的加和部分,到这里为止,这个程序的一般性优化也做完了,因为程序也很简单,所以很多的方面都无法体现,比如之前提过的GPU运算的一大问题在于精度,还有尽可能减少访存这些方面的优化都没有体现出来,所以下一篇博客我们准备真正向应用CUDA靠拢,去进行矩阵计算~
希望我的博客能帮助到大家~
参考资料:《深入浅出谈CUDA》
- CUDA编程(八)树状加法
- CUDA编程(八)树状加法
- CUDA编程之树状加法
- CUDA之并行算法系列(一)树状加法
- CUDA编程:向量加法
- 《GPU高性能编程CUDA实战》学习笔记(八)
- 【CUDA并行编程之八】Cuda实现Kmeans算法
- 【CUDA并行编程之八】Cuda实现Kmeans算法
- CUDA(34)之算数加法
- CUDA编程入门:向量加法和矩阵乘法
- GPU编程之CUDA(八)——示例程序运行截图【3_Imaging】
- cuda向量加法时间
- CUDA二维矩阵加法
- CUDA矩阵加法
- CUDA编程(1)
- CUDA编程->CUDA入门了解(一)
- CUDA程序优化小记(八)
- CUDA 学习(八)、线程块调度
- 使用USB3.0的U盘装Win7教程
- 三层交换机--访问本地VLANIF接口所需要的L3表项
- oracle数据库中varchar2陷阱
- Android开发 之 Activity全透明渐变切换
- Ubuntu14.04 ROS(indigo) 激光雷达(Sick LMS511) 数据读取
- CUDA编程(八)树状加法
- AS初来乍到Kotlin的配置
- android项目结构设计参考
- B-树、B+树、B*树的区别
- Codeforces 825 A Binary Protocol
- 性能分析神器VisualVM
- 不同的路径 -LintCode
- JAVA (集合和数据结构)
- 百度统计 tongji API 的使用