CUDA学习笔记(7) GPU内存分级

  在NVIDIA的GPU中,内存(GPU的内存)被分为了全局内存(Global memory)、本地内存(Local memory)、共享内存(Shared memory)、寄存器内存(Register memory)、常量内存(Constant memory)、纹理内存(Texture memory)六大类。这六类内存都是分布在在RAM存储芯片或者GPU芯片上,他们物理上所在的位置,决定了他们的速度、大小以及访问规则。

  先总体地看下一张显卡的硬件组成。用我电脑上的“Quadra K620”显卡举例,如下图,整张显卡PCB电路板上的芯片主要可以分为三类:

  1. GPU芯片,也是整张显卡的核心,负责执行计算任务。
  2. DDR3存储芯片,其在显卡中相对与GPU的地位相当于电脑中内存条对于CPU,只是放在了显卡上专供GPU使用。
  3. 时钟、电源等其他辅助功能的芯片。

CUDA学习笔记(7) GPU内存分级

   显卡的内存可以分为GPU片内(On-Chip)存储体和位于DDR3存储芯片中的存储体。片内存储体的访问延迟(Latency)远低于片外存储体的访问延迟(Latency),当然片内存储体也有更快的传输速度。

  • 全局内存(Global memory),位于片外存储体中。容量大、访问延迟高、传输速度较慢。在2.X计算力之后的GPU上,都会使用二级缓存(L2 cache)做缓冲,达到较快的传输速度,但这并不能减少访问的延迟(Latency)。

    • 所有数据都必须先传入到这里,或者从这里传出。
    • 用户通过调用CUDA函数控制全局内存的分配空间、传入数据、传出数据。例如:
    函数名 功能
    cuMemAlloc 内存分配
    cuMemAllocPitch 内存分配,并将长度补全到2n
    cuMemFree 释放内存
    cuMemcpyHtoD 传输数据,从内存到内存
    cuMemcpyDtoH 传输数据,从内存到内存
    cuMemcpyDtoD 传输数据,从显卡A到显卡B
    cuMemsetDn 内存按n位初始化,其中n=8,16,32


  下面详细介绍下GM107中SMM的内部结构:
CUDA学习笔记(7) GPU内存分级

  指令缓存(Instruction Cache)存放核函数中的的指令。
  指令缓冲区(Instruction Buffer)Core访问它的速度比指令缓存更快,但是空间更小。
  线程束调度器(Warp Scheduler)调度各个线程束执行、挂起等。
  流处理器(Core 或 Stream Processor)
  一级缓存(L1 Cache)、纹理内存(Texture),他们公用同一片cache区域,可以通过调用CUDA函数设置各自的所占比例。
  共享内存(Shared Memory)
  寄存器区(Register File)供各条线程在执行时存放临时变量的区域。


  • 本地内存(Local memory),一般位于片内存储体中,在核函数编写不恰当的情况下会部分位于片外存储器中。当一个线程执行核函数时,核函数的变量、数组、结构体等都存放在本地内存(Local memory)中。此时存在两种情况:
    • 当我们核函数中的变量较少,寄存器区(Register File)的大小足够放下这些变量,那么他们就放在GPU芯片的流处理器组(SM)中的寄存器区。
    • 当核函数中有大数组、大结构体以至于寄存器区放不下他们,编译器在编译阶段就会将他们放到片外的DDR3芯片中(最好的情况也会被扔到L2 Cache中),且将他们标记为“Local”型。核函数在执行时会反复读取他们,这也就大大降低了核函数运行效率,这是我们一定要避免的情况!

《CUDA Programming Guide》中重点提到:

  An automatic variable declared in device code without any of the device, shared and constant memory space specifiers described in this section generally resides in a register. However in some cases the compiler might choose to place it in local memory, which can have adverse performance consequences as detailed in Device Memory Accesses.

  • 共享内存(Shared memory)位于每个流处理器组中(SM)中,其访问速度仅次于寄存器,特点是一个线程块(Block)中的所有线程都可以访问。主要存放频繁修改的变量。

  • 寄存器内存(Register memory)位于每个流处理器组中(SM)中,访问速度最快的存储体,用于存放线程执行时所需要的变量。

  • 常量内存(Constant memory)位于每个流处理器(SM)中和片外的RAM存储器中。常量内存是只读的,不能在核函数执行的过程中被修改。但是可以在核函数执行前,通过重新传入数据进行修改。

    • 当常量内存被初始化的时候,它会先全部保存在片外的RAM中,然后使用8KB(具体数值随显卡型号变化)的一级缓存 作为缓冲,可以将访问延迟(Latency)缩到很短。
    • 当一个线程束中的各个线程访问的不是一段连续的内存时,如果访问的是全局内存,则可能会访问多次,造成时间的浪费;但如果访问的是常量内存,只要访问的数据是在一级缓存内,则立刻取得数据。

  A request is then split into as many separate requests as there are different memory addresses in the initial request, decreasing throughput by a factor equal to the number of separate requests. The resulting requests are then serviced at the throughput of the constant cache in case of a cache hit, or at the throughput of device memory otherwise.

  • 纹理内存(Texture memory)位于每个流处理器(SM)中和片外的RAM存储器中。它与常量内存非常类似。但是他有两点不同:
    • 纹理内存,顾名思义就是专门用于纹理贴图操作的,故在该操作上使用纹理内存更加高效。
    • 纹理内存具有广播机制。

  Reading device memory through texture or surface fetching present some benefits that can make it an advantageous alternative to reading device memory from global or constant memory:
  1. If the memory reads do not follow the access patterns that global or constant memory reads must follow to get good performance, higher bandwidth can be achieved providing that there is locality in the texture fetches or surface reads;
  2. Addressing calculations are performed outside the kernel by dedicated units;
  3. Packed data may be broadcast to separate variables in a single operation;
  4. 8-bit and 16-bit integer input data may be optionally converted to 32 bit floatingpoint values in the range [0.0, 1.0] or [-1.0, 1.0]


CUDA学习笔记(7) GPU内存分级
  第一句话比较难度,大致意思是在进行纹理贴图操作或几何表面访问的操作时,由于访问数据的方式不符合访问全局内存和常量内存的访问规律,导致访问速度下降,但是纹理内存可以解决这一问题。





  最近七篇博客是我的CUDA学习笔记的基础部分,接下来我准备巩固OpenGL的基础知识,然后学习OpenGL的进阶部分:缓冲区对象等。并结合CUDA使用,实现一些计算机视觉、计算机图形学方面的算法及论文。





参考:

1.《CUDA并行程序设计》机械工业出版社

2.CUDA Toolkit Documation

3.GM107芯片白皮书 NVIDIA GeForce GTX 750 Ti Whitepaper