CUDA 学习(十一)、共享内存

来源:互联网 发布:mysql中if exists用法 编辑:程序博客网 时间:2024/05/29 09:48

一、概述

       共享内存实际上是可受用户控制的一级缓存。每个SM 中的一级缓存与共享内存一个64KB 的内存段。在开普勒架构的设备中,根据应用程序的需要,每个线程可以配置为16KB 的一级缓存或共享内存。而在费米架构的设备中,可以根据喜好选择16KB 或 48 KB 的一级缓存或共享内存。早期的费米架构的硬件(计算能力为1.x)中只有固定的16KB 共享内存而没有一级缓存。共享内存的延迟极低,大约有1.5 TB/s 的带宽,远远高于全局内存的190GB/s, 但是它的速度只有寄存器的1/10。

      实际上,仅看带宽数据,共享内存的带宽为1.5 TB/s,全局内存的带宽最高为190 GB/s,比率为7:1 。换言之,有效使用共享内存有可能获得7倍的加速比。毫无疑问,共享内存是所有关心性能的CUDA 程序员应该认真掌握的一个概念。

     然而,GPU 执行的是一个内存加载/ 存储模型,即所有的操作都要在指令载入寄存器之后才能执行。因此,加载数据到共享内存与加载数据到寄存器中不同,只有当数据重复利用,全局内存合并,或线程之间有共享数据时使用共享内存才更合适。否则,将数据直接从全局内存加载到寄存器性能会更好。


二、共享内存存储体

       共享内存是基于存储体切换的架构(bank-switched architecture)。在费米架构设备上有32个存储体,而在G200与G80 的硬件上只有16个存储体。每个存储体可以存放4个字节大小的数据,足以用来存储一个单精度的浮点型数据,或者一个标准的32位的整型数。开普勒架构的设备还引入了64位宽的存储体,使双精度的数据无须再跨越两个存储体。无论有多少线程发起操作,每个存储体每个周期只执行一次操作。因此,如果线程束中的每个线程访问一个存储体,那么所有线程的操作都可以在一个周期内同时执行。此时无须顺序地访问,因为每个线程访问的存储体在共享内存中都是独立的,互不影响。

       注意,线程访问共享内存需要排队等待,当一个线程访问共享内存时,线程束中的其他线程将阻塞闲置。这方面一个很重要的问题就是延迟并没有因为切换到另一个线程而得到隐藏,事实上我们将整个SM都阻塞了。由于SM 会处于闲置状态直到存储体请求被满足,因此,存储体冲突应尽可能地避免。

                      

        bank冲突:最糟糕的情形就是所有的线程都对同一个存储体执行写操作,这将导致对同一存储体顺序进行32次访问操作。典型地,当跨服超过32时,就会发生多个线程访问同一个存储体。当跨幅以二的幂次倍减少时,也会发生这种情况,连续的轮询将导致更多的存储体冲突。


三、避免存储体冲突

       通过将数据集以每行32个元素方式在共享内存中进行划分,然后线程以列的方式进行访问,我们可以获得零存储体冲突的内存访问。

           

  








0 0
原创粉丝点击