CUDA-Memory Local/Constant/L1/L2/Register

Local Memory

Local memory 的命周期是一个thread,它存在与Global memory中,所以对Local memory的访存是低效的。

Local memory 是存储自动变量的。通常自动变量是较复杂的structures 或者数组,这些对象都会消耗太多的这个线程的寄存器。当nvcc编译器发现没有足够的寄存器空间来保存变量时,就会将变量放进Local memory中。

有个技巧,如果一个kernel函数中需要使用数组,而且数组的长度是固定的,为了避免使用Local memory,将这个数组拆成单个的变量,这些变量会被存储到Registers中(当然是当Registers的个数足够时)。

Constant Memory

在Device中共有64KB大小的Constant memory,只读。

如果一个warp中的所有threads访问同一个Constant memory地址时,此时的访存可以和Registers一样快。所以对于只读的全局数据,选择放在Constant memory中。

Registers

通常,访问寄存器每一条指令都不会消耗额外的时钟周期,但由于寄存器的读写依赖关系和寄存器内存bank冲突,可能会出现延迟。

编译器和硬件线程调度程序将尽可能优化调度指令,以避免寄存器bank冲突。应用程序无法直接控制这些银行冲突。这些开发者不受控制。

当没有足够的寄存器可用分配给指定任务时,就会出现寄存器压力。尽管每个多处理器都包含数千个32位寄存器但它们都是在并发线程之间分配的。为了防止编译器分配太多寄存器,使用-maxrregcount=N编译器命令行选项(nvcc)或启动边界内核定义限定符来控制每个线程分配的寄存器的最大数量。

L1/L2 Cache

每个SM有自己独有的一片存储空间,这个空间被Shared memory和L1 缓存公用。而L2缓存 被所有SM公用,L2中缓存来自Global Memory中的数据。

SP访存时,先从L1中找数据,若没有则从L2中找,若仍没有,从Global Memory中找。

线程连续访问(Coalesced Access)的好处,是所需的数据很可能已经缓存在L2上,cache hit,此时的访问速度很快。回顾卷积的例子。

Allocation

使用cudaMallo() 和cudaFree()在Device上申请, 和回收空间是很耗时的操作,所以程序应该尽可能重复利用已分配好的空间。