CUDA编程3 共享内存的应用

81 阅读2分钟

矩阵乘法中通过将矩阵切分为多个子矩阵并加载的共享内存来加速矩阵乘法的计算。

其中子矩阵的大小为tile_width,如何选择tile_width的大小呢?

1、如果tile_width设置的太大会怎样?

(1)可能会超过一个块允许的最大线程数。 比如在G80架构和GT200架构最多允许512个线程

(2)可能会超过shared memory的极限。 比如每个block给一个矩阵申请16 * 16 * 4的内存,共计1KB.两个矩阵就是2KB.这样每个block需要2KB的内存。 每个SM的shared memory是有限的,能够同时驻留的thred blocks也是有限的。Shared memory size = blocks number * shared memory assigned per block。如果tile width太大,意味着shared memory assigned per block变大,相应的总shared memory size不变,一个SM中能够驻留的block就变小了。(问题是:如果我分配的shared memory特别大会发生什么??)

比如下图中3090共有82个SM,一共有10496KB的的shared memory,10496KB / 82 = 128KB,即每个SM有128KB的shared memory。

tmp5675.png

tmp4F51.png

注意:在3090中一个SM中包括两个Special functional unit。一个warp scheduler就表示该硬件能够执行一个warp。(注意 不是驻留一个warp,应该是可以驻留多个warp,也就是active warp可以有多个,但是处在execute状态的和warp scheduler的数量对应。)下面这个图,有4个warp scheduler,能同时执行4个warp。

tmp10EA.png

2、shared memory带来的好处 (1)减少了global memory访问的次数。 原来:每个线程访问2N次。共计2N * Nd * Md 现在:每个线程访问2N/tile次。共计2N/tile * Nd * Md 所以,对global的访存次数减少tile倍。再加上对shared memory的访问时间。

解释:

1、原来每个grid的线程要访问所在行和所在列的所有元素,假设矩阵为大小为Nd * N 和 N * Md。这样每个线程相当于要访问2N次,共计Nd * Md个线程,总计2N * Nd * Md次。

2、增加shared memory之后,访存发生在线程将元素加载到shared memory的过程中。所有每个线程负责自己所在行的子矩阵的对应位置的元素和所在列的子矩阵的对应的位置的元素的process that loads ele to shared memory。这样每个线程的访存次数就变成了2 * N / tile 次。共计Nd * Md个线程,所以要访存次数减少了tile次。

访存次数少了,降低程序的带宽要求。

CUDA API中的原子操作 Atomic Functions

atmoic+

Add() Sub() Exch() Min() Max() Add() Dec() CAS() // 算数运算

atomic+

And() Or() Xor() //位运算