cuda pdf gpu编程_CUDA优化的冷知识11 |一些规避的坑和优化的要点

75e87cf0ba5a31807ef91c50d1759ee3.png

这一系列文章面向CUDA开发者来解读《CUDA C 

Best Practices Guide》 (CUDA C最佳实践指南)

大家可以访问:

这是一本很经典的手册。

CUDA优化的冷知识|什么是APOD开发模型?

CUDA优化的冷知识2| 老板对不起

CUDA优化的冷知识 3 |男人跟女人的区别

CUDA优化的冷知识 4 | 打工人的时间是如何计算的

CUDA优化的冷知识 5 | 似是而非的计时方法

CUDA优化的冷知识 6 |GPU端的CUDA Event计时

CUDA优化的冷知识 7 |GPU端Event计时的重要特色

CUDA优化的冷知识 8 |GPU显存的特色

CUDA优化的冷知识9 |GPU显存的粒度

CUDA优化的冷知识10 | GPU卡和Jetson上显存优化的特色

bc0cc27d8f71bc610565e2a9bc06bb01.png

我们的CPU在读取的时候, 从它的内存读取到它的L2的时候(L3或者L4, 作为LLC, 很多时候是victim cache, 也就是读取的时候不经过, 只有被淘汰的数据才尽最大挽留的存放, 所以这里不提), 粒度往往是至少64B的,这样, 同样零散的分布的读取1B的数据, GPU效率是1/32, 而CPU可能只有1/64. 更加可怕的是, CPU往往会对邻近的cache块/行, 进行预读, 和预测性的预读 实际上很可能会导致, 读取1B, 传输了上百B甚至更多的情况, 此时从效率来说, GPU的1/32要远远超过了CPU. 更何况, 这个是从效率上的说法, 实际能有效提供的带宽, 要用效率乘以各自的峰值, 显存具有大得多的峰值, 此时再乘以更高的效率,就得到了在这种严重不适合GPU, 也不适合CPU的情况下, GPU的性能依然要更好的情况出现. 这点很多书上往往进行了忽略. 因为这些书教育我们, 一定要使用合并性的访问, 要使用适合GPU的访问. 从而导致了很多用户, 不敢将这种不适合GPU的访存, 进行CUDA化改写, 这是很错误的。

本实践手册的这个章节, 破除了这个迷信思想, 还是需要的. 特别的, 在我们的jetson产品上, 存储器的体系结构(hierarchy), 是缺乏一个主芯片级别的统一最后一级缓存的, 即所有的数据, 都最终要通过存储器(LPDDR4), 才能得到一致. 哪怕此时问题来说, 同样的一个渣代码, 无论用CPU还是迁移到GPU上, 访存都是很零散的, 用户你究竟是准备用自带的ARM CPU核心来读取呢? 还是准备用集成的GPU部分来读取呢?

前面的话题已经说了, 如何在Global Memory做, 以尽量取得较好的性能优势. 以及, 和传输相关的方面的话题. 但是有一点没有说, 就是8.0+计算能力新引入的, 将部分Global memory中的缓冲区, 形成一个较长时间片段内, 锁定在L2 Cache中的效果. 或者用户可以理解成, 在一定的时间范围内, 将L2的某部分设定成尽量类似L1之于shared那样的, 类似手工管理, 或者说缓慢淘汰的效果. 这个不说是因为我们还没有测试, 同时, 我们所有在售的Jetson产品都不支持这个特性. 我们可能在最后的时候, 在8.0+上进行测试, 然后重新说这个话题。

好了. 先进行今天的内容. 今天的内容是如何尽量发挥shared memory的性能. 这个其实也是老生常谈了. 要发挥shared memory的性能, 我们得知道为何我们要用shared memory, 为何它的性能是在某些特定的kernel中, 是性能影响因素,因为你既然读到这里, 如果你的kernel本身不卡在shared memory性能上, 甚至根本连shared memory都不会用到, 则自然你不继续看了, 如果你需要继续看, 则至少你已经用了shared, 或者想用, 并且想解决使用中的性能瓶颈, 或者提前避开一些坑. 所以我们就说点这些。

如同本实践手册所说, shared memory在某种意义上, 等于是手工管理的L1 cache. 这种说法, 对于来自CPU的用户来说, 听起来还是比较有吸引力的.

因为一个传统的L1 cache你只能被动的使用它, 并且预估自己的那些访存模式, 适合被L1缓冲, 从而尽量的去好好使用. 而Shared Memory作为完全用户管理的东西, 你有充分的自由可以随意使用, 任何情况下都不会像L1那样, 数据存在自动淘汰可能, 总是可以安全的存储, 高速的使用的.

但是我们作为GPU, 一个追求吞吐率的设备(上次说过的), 很多时候用户们追求近乎100%的压榨出来上面的某些单元的性能, shared也不例外。

今天就大致说了一下, 哪些是影响因素, 并再次(再N次)的给出了使用shared memory进行分块矩阵乘法和转置的例子, 用来显出使用了shared后的高速度来。

我们直接说一下一些规避的坑,和优化的要点:

第一点则是, 尽量规避shared memory上的bank conflict. 这个也是老生常谈了. 我们现在用的, 能买到的新卡, 都是4B宽的Bank. 每个Bank用户应当理解成在每个周期内, 能独立给出4B数据的独立单元,这样每个SM里面, 如果有32个Banks的话, 能给出128B/周期的性能. 这个还是很惊人的,因为对于从CPU迁移过来的老代码来说, 自家的L1 cache, 也不过常见每个周期能给出2个32B读取, 和1个32B写入这种. 也就是96B/周期. 但是CPU的核心数才多少, GPU的SM数量又多少。

一个动辄80多个SM的GPU, shared能聚合给出10TB+到20TB+/s的性能(假设频率从1Ghz~2Ghz的GPU主频). 所以很多老代码, 进行了优化, 迁移到GPU后, 第一步就是考虑尽量利用shared的这个高速特性. 从而发挥性能. 然而, 这个高速度只是理想状态, 一旦shared发生了bank conflict后, 性能会下降的. 下降的程度和你bank conflict的程度有关系. 而具体bank conflict是什么, 我们这里不讲. 因为实在是讲的太多太多次了(几十次是有了). 感兴趣的用户可以回看我们的编程手册内容, 或者回看Sisiy的阿三书. 里面都扯了好多好多。

这里主要说的一点是, 在近期的NV给出的资料中, 揭露了一个新的现象.

就是我们以前一直说Bank Conflict的时候,根据手册,都是用的warp整体(在现在你能买到的卡上), 作为bank conflict分析的, 也就是32个线程内部之间的有无bank冲突. 从而尝试优化. 但是这种手册上给出的分析方法, 和实际的使用中的profiler给出的conflict的报告, 和实际因为达到的性能, 很多时候是理论和实际结果不符合的。很多情况下, profiler给出的bank conflict数量要少很多, 性能指标也要好很多.

例如本论坛的这个例子:

bbs.gpuworld.cn/index.p

该例子的楼主们, 以及, 奈奈同学, 给出了自己观察到的不同于手册说明的现象. 并且进一步的挖掘出来了, NV只在GTC上给出的一个PDF资料. 该资料里有不同于手册的说法: 即: 在8B, 16B的这种非4B的访问情况下, 也就是类似float2, float4, double, double2这种访问的情况下, bank conflict的计算不是按照warp进行的, 而是分别实际上按照half-warp和1/4 warp进行的. 这点符合实际实践中的profiler的报告的性能结果. 我们今天在这里额外的从论坛揪出这个案例, 同时用NV的这个资料, 进行说明:

在特定的访存方式下, bank conflict的计算应当采用另外的范围(即1/4或者1/2的warp), 而不是从warp整体. 当读者或者用户正好使用这种访存方式的时候, 无需过度的去考虑优化Bank Conflict的问题, 因为很可能此时conflict根本就不存在.

这点需要注意了. 此外, 我们还想给出一点说明的是, 有的时候, 将shared memory作为一个高速的查找表的时候(参考我们之前编程指南手册说过的, shared memory的三大用途之一), 如果下标高度规律性的一致, 在warp内或者block内部如此, 则编译器可能会生成另外一种带有LDS.U后缀的shared读取指令, 会让实际的读取的延迟降低很多, 等效吞吐率提升很多. 该现象很容易发现, 也不报告任何的bank conflict. 但是我们目前还不知道为何会这样, 以及, 如何能让编译器触发这点. 这里的给出只是用来说明, 很多时候本实践手册中的conflict方面的相关优化并不成立, 用户应当以实际的应用中的性能分析器对相关单元的指标报告为准. 然后手册今天不出乎意料的, 继续引入了矩阵乘法/转置的内容, 用来说明shared memory在重复使用数据, 和转换不适合的低效global memory的访存为适合的高效的shared memory访问的特点.

重复使用数据就不用说了, 既然shared memory作为手工管理的L1 cache, 他自然也有cache的这种提供缓冲和高速性能的特点; 而转换不当的访存模式(例如常见的纵向坐标优先或者说大跨步等的方式), 经过shared中转了一次, 变成了恰当的模式, 则用户应该看一下. 后者是很多用户容易忽略的, 特别是对于一些案例, 数据明明只需要使用1次, 那么为何我还需要先读到shared memory中缓冲一下, 然后再从shared memory读取一下呢? 因为对于很多这种的, 哪怕你只用一次, 经过shared memory这么一倒腾, 就可以让访存模式理顺很多, 哪怕只用一次, 也是有性能优势的. 而这种优势, 在直接使用L1 -- 不具有不同深度的bank的同时数据供应 -- 是做不到的. 但是shared可以。

e34ddba5d69a6591e16662b9b04cd299.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CUDA programming: a developer's guide to parallel computing with GPUs. by Shane Cook. Over the past five years there has been a revolution in computing brought about by a company that for successive years has emerged as one of the premier gaming hardware manufacturersdNVIDIA. With the introduction of the CUDA (Compute Unified Device Architecture) programming language, for the first time these hugely powerful graphics coprocessors could be used by everyday C programmers to offload computationally expensive work. From the embedded device industry, to home users, to supercomputers, everything has changed as a result of this. One of the major changes in the computer software industry has been the move from serial programming to parallel programming. Here, CUDA has produced great advances. The graphics processor unit (GPU) by its very nature is designed for high-speed graphics, which are inherently parallel. CUDA takes a simple model of data parallelism and incorporates it into a programming model without the need for graphics primitives. In fact, CUDA, unlike its predecessors, does not require any understanding or knowledge of graphics or graphics primitives. You do not have to be a games programmer either. The CUDA language makes the GPU look just like another programmable device. Throughout this book I will assume readers have no prior knowledge of CUDA, or of parallel programming. I assume they have only an existing knowledge of the C/C++ programming language. As we progress and you become more competent with CUDA, we’ll cover more advanced topics, taking you from a parallel unaware programmer to one who can exploit the full potential of CUDA. For programmers already familiar with parallel programming concepts and CUDA, we’ll be discussing in detail the architecture of the GPUs and how to get the most from each, including the latest Fermi and Kepler hardware. Literally anyone who can program in C or C++ can program with CUDA in a few hours given a little training. Getting from novice CUDA programmer, with a several times speedup to 10 times–plus speedup is what you should be capable of by the end of this book. The book is very much aimed at learning CUDA, but with a focus on performance, having first achieved correctness. Your level of skill and understanding of writing high-performance code, especially for GPUs, will hugely benefit from this text. This book is a practical guide to using CUDA in real applications, by real practitioners. At the same time, however, we cover the necessary theory and background so everyone, no matter what their background, can follow along and learn how to program in CUDA, making this book ideal for both professionals and those studying GPUs or parallel programming.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值