前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CUDA优化的冷知识14|local memory你可能不知道的好处

CUDA优化的冷知识14|local memory你可能不知道的好处

作者头像
GPUS Lady
发布2021-02-05 11:26:37
1.3K0
发布2021-02-05 11:26:37
举报
文章被收录于专栏:GPUS开发者
这一系列文章面向CUDA开发者来解读《CUDA C Best Practices Guide》 (CUDA C最佳实践指南)

大家可以访问:

https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html 来阅读原文。

这是一本很经典的手册。

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

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

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

CUDA优化的冷知识11 |一些规避的坑和优化的要点

CUDA优化的冷知识12 |一些规避的坑和优化的要点(续)

CUDA优化的冷知识13 |从Global memory到Shared memory

下面我们简单的再说一下local memory.

首先要注意的是local memory并不local, 它实际上依然是一段显存. 但可能会被各级相关的缓存所缓冲。

主要用途有两点:

一点是你(读者)使用,当你需要每个线程的一段缓冲区的时候,你并不需要单独的开一个全局的大的缓冲区,然后作为参数传递给kernel, 让kernel里的每个线程找到自己对应的一部分使用。你可以直接来一个局部的大数组(不能过大!), 享受类似以前的CPU上的C风格的, stack上的定义的数组, 或者类似CPU上的alloca()的分配风格, 能自动的每人一份, 而且能自动释放, 很是方便,而且不仅仅如此, 你如果传递进来一个大缓冲区这样用, 你需要为所有的一次启动的线程分配缓冲区. 而用local memory, 则只需要保证能真正同时上到SM里执行的那些线程的数量所需要的缓冲区,举个例子说, 前者你启动了1M个线程, 每个线程需要1KB, 则你需要1GB的显存提前手工分配了.而如果你使用后者, 某GPU device实际上只能同时执行10K个这样的线程, 其他的暂时没上的, 在其他block中的线程们, 会等待下次轮批次再上, 则硬件上只需要准备/分配出来100MB的显存, 即可应付, 因为这些线程不是真的"同时"在运行中的(具体参考我们之前的编程指南手册).这点不仅仅降低了手工管理的成本, 还降低了你花钱买一张更大显存的卡的成本.特别的是在Jetson设备上, 显存(内存)容量有限, 用户应当考虑这点.但是很遗憾的, 很多人就是喜欢传递过来一个额外的数组/指针这样使用, 原因我们还未知。

此外, 使用local memory还有一个好处, 就是虽然它像global一样, 被各级缓存缓冲, 但是它有更精细的缓存控制策略, 可以允许对local memory上特定位置的访问, 标记成discard, 或者说last use(PTX手册用语). 允许cache直接将对应的cache line内容, 就地丢弃掉, 而无需必须回写下一级缓存甚至到显存. 这点作为global memory是做不到的。

此外, 今天的实践手册没有说明的是, local memory还具有强制合并访问的特性.我们都说用了local memory, 但是几乎没人讨论"local memory是否是合并的", 既然我们今天已经知道了它也是用的显存模拟出来的, 为何不讨论这点?这是因为local memory有自动交错的特性. 例如我们定义了一个int dog[N]; 假设dog被编译器选择放置到了local memory上, warp中的每个线程都在访问同样下标的, 例如dog[K]的时候, 实际上来自32个线程的同样下标的访问会被合并成连续的地址空间上排布的一段128B的内容, 非常的合并.用户可以理解成local memory实际上总是按warp排布的, 任何int dog[N]都是内在的被存储为int _dog[N][32]这种自动交错.

从而总是自动形成了, 当下标一致的情况下, 自动合并的效果. (这点最早见于2013年的CUDA Handbook, 这是一本好书, 但是国内翻译的书质量不高,所以我们一直没推荐。也可以参考我们之前的CUDA编程指南中的内容),因为这种自动交错/合并的存在. 对local memory中, 来自同一个warp的杂乱的下标/指针访问这种, 应当避免. 因为默认是一致的. 杂乱的访问会导致访存被拆分成多次请求, 严重降低效率.这是local memory的用途一.用途二则是, 方便编译器安排一些无法有效的放入寄存器, 例如当前阶段寄存器资源用的太多了, 或者一些访存方式(例如对寄存器试图进行下标索引---N卡不支持这种), 不能放入.

所以这是为何今天手册说, 对局部的数组的非编译时刻能确定的下标访问, 会被放入local的原因. 也就是动态的下标访问.所以我们其实是可以用数组的, 只要(1)数组别太大, 或者很大, 但是每段代码使用的热点明显. (2)下标是静态的, 编译时刻能确定的. 则往往数组也会整体或者其中的部分元素, 被编译器安排到寄存器中.寄存器和local memory中的内容的交换, 是一个动态的过程。某些时刻某个变量可能在寄存器中, 但下一时刻它可能在local中, 根据不同代码位置的寄存器压力, 或者其他因素所决定.好了.

到现在为止,我们已经说了shared memory和local的内容, 这些都是相比CPU迁移过来的代码来说,所具有特色和需要优化的时候注意的东西. 建议总是小心对待.代码这玩意, 你先写对为主, 然后再考虑优化.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GPUS开发者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档