GPU 体系结构与 CUDA 编程模式

GPU体系结构初探,思路比较混乱,但整体知识点还是都涵盖了的。

一、 CUDA 构成

CUDA 构成

CUDA 体系结构

Host: CPU(任务发送、组织、预处理、管道并行)

Device: GPU(并行计算)

1. Memory

CUDA中的可以使用的内存种类及体系结构:

最大功能执行单元硬件体系图

  • 一个 SM 中包含 16384 * 32 bits = 64KB Register Files,其中包含 16K 个 Entry,每个 Entry 4 Byte,用来存放单精度浮点数或者整数,而双精度浮点则需要占用相邻两个 Entry。
  • 程序中定义的局部变量一般都是分配在 Register 中,当 Register 不够用或无法确定变量大小时,就分到 Local Memory 中。
  • Constant Memory 与 Texture Memory 都位于显存中,只读。Constant L1 Cache 8KB,Texture L1 Cache 8KB,此外,在 SM 之外,还有 256KB 的 Texture L2 Cache 和 Instruction & Constant L2 Cache。
  • Global Memory 和 Local Memory 存在于显存中,没有 GPU 片上 cache,故而访问速度很慢。

Memory 读取过程中的层级情况如下图所示:

Mordern GPU Memory Hierarchy

2. Thread 组织

计算框架

CPU 将任务组织成 Kernel,并对 Kernel 进行处理,使其 符合 GPU 体系结构 ,然后通过 CUDA 丢给 GPU 执行。

Thread 组织层次

  • 同一 thread block 内的 thread 可通过 shared memory 共享数据,每个 shared memory 最高可包含 512 个线程。
  • 拥有同样维度同样 kernel 的 thread block 被组织成一个 grid,grid 是 CUDA 处理任务的最大单元。
  • 执行时,SM 一次最多会处理 8 个 thread block 或 1024 个 thread(看两者中哪个更小),然后内部将这些 thread 组织成 warp。每个 warp 包含 32 个 thread。这是因为 SM 一次最多可 同时 处理 32 个 thread(为什么是 32 个呢,因为 SP 的执行延迟一般是 4 个周期,类似执行管线长度有 4 级,所以每个 SP 同时可以执行 4 条相同的指令来充分消除这些延迟,而一个 SM 中有 8 个 SP,所以可以同时执行 32 个指令)。

3. SIMT 编程模型

以 GT200 为例。GT200 由 10 个 Thread Porcessing Cluster (TPC) 组成第一级核心计算框架,而每个 TPC 由 3 个 Streaming Mutiprocessor (SM) 加一个纹理硬件管线构成,纹理硬件管线又包含了 8 个 Texture Filter 和 4 个 Texture Address Generator,每个 SM 都有单独的 Front-End,包括取指、解码、分发逻辑和执行单元等等。

另外需要注意的是,GPU 制造厂商所宣称的 core 其实是指 “ALUs/FPUs”,跟 CPU 中提到的 core 还是有区别的,一般 NVIDIA 或 ATI 称 core 为 SP (streaming processor) 或者 SC (shader core) 或者 TP (Thread core)。在 GT200 中,一个 SM 中包含 8 个 SP,但这些 SP 并不完全是独立的执行单元,每个 SP 虽然有单独的 regiter file,独立的指令指针,但 SP 并没有完整的独立 front-end,比如取指和指令派发,因此 SP 更像是CPU中执行部分。另外每个 SM 还包含 2 个 SFU (Super Function Unit) 和一个 DPU(Double Presision Unit)。

很显然 SM 没有分支预测部分,也没有错误恢复机制,SM 假设没有随机执行的指令。所以在遇到分支的时候,SM 必须等待分支跳转地址计算结束,才取下面的指令,而后才开始继续工作。NVIDIA 称其执行模式了单指令多线程——SIMT。

二、 CPU Cache 相关

CPU Memory Hierarchy

Intel 多核 CPU 构成:

1. Memory 的使用策略

Intel CPU 对内存的使用策略多种多样,分别用来满足不同的应用需求:

A. 强不可缓冲模式(strong uncacheable)

B. 不可缓冲模式(uncacheable)

C. 写透模式(write through)

D. 写回模式(write back)

E. 写组合(write combining)

F. 写保护(write protection)

我们常见的是 D 和 E,普通应用程序执行的内存模式都是 WB,AGP 内存是 WC,而内存使用模式的定义可以通过 PAT,MTTRs 等进行配置。

Intel CPU 按访问模式将数据分为 3 类:

  • 临时的,表示在不久马上就会用到的;
  • 连续的,表示数据会被序列访问的;
  • 非临时的,表示接下来一段时间不会被再次用到。

WB 内存模式最主要针对第一种使用模式的数据设计,经常被反复读写,所以 WB 模式会完整利用 Cache 来加速这样的访问。而 WC 模式主要是针对写的非常多,而基本不怎么读的数据,比如 AGP MEMORY,这样数据就不必经过 Cache 缓冲,经过 Cache 反而会影响效率,因为同时要写 Cache 和内存,造成不必要的 cache 污染和 cache 波动。

L2 cache is tied to four 64-bit memory channels, L1 caches store unique data for each SIMD to raise caching efficiency. L2 cache size has been doubled proportionally to the increased number of TMUs – it’s reached 128KB per memory controller.

2. Cache 原理

Intel CORE2 实现了 L1 和 L2 两级 Cache,L2双核共用,CORE2 的 2 级 Cache 都是 64 Bytes/Line,共 1024 行。L1 Cache 为 8 路组相联,L2 Cache 为 16 路组相联。

Cache 在与主存之间传输数据采用 8-Transfer Burst transcation,(Cache Line 不支持 partial write transaction)即一次传输 64 字节,分 8 组突发传送,为原子操作,即便只需要小于 64 字节的数据,总线依然传送 64 字节填充整个 Cache Line。

根据 CPU 的档次,L2 Cache 大小不一,但 L1 都是64KB,32KB 存储指令,即L1I;32KB 存储数据,即L1D。两个 Core 的 L1D Cache 之间可以互相传输数据。L1 Cache 拥有几个数据和指令硬件预取器,L2 Cache 的预取器是基于 L1 Cache 预取器的访问模式和密集程度来工作的,使用了一个改进的 Round-Robin 算法动态的在两个处理器之间分配带宽。前端总线接口也采用了类似的方式以确保平衡。L1 Cache 和 L2 Cache 采用了独立访问设计,也就是说 Core 可以从 L2 L1 或 Main Memory 中 直接 取数据,无须逐级上升。Intel Cache 使用了 mainly inclusive 设计。//what’s mainly inclusive?

Cache Line 构造:

Cache 使用策略常用算法://需要再理解

  • 全相联:内存中的任何 64 对齐字节可以放在任何一 Cache Line 中
  • 直接映射:内存的任何一个 64 对齐字节只能放在特定的一 Cache Line 中
  • N 路组相联:内存的任何一个 64 对齐字节只能放在某些 (N) Cache Line 中

L1 为 8 路组相联,表示每 8 条 Cache Line 对应 128 个组值中的一个。组值为 7 bit。很显然如果 L1D 为 32KB 并且为 8 路组联,则每 128 个 64 组组织重复一次。

我们详细理一下 Intel CORE2 L1 Cache 的参数:64KB、64B/Line、8路组相联。所以我们可以知道 Cache Line 数目为 1024 行,每 8 行编为一组,共 128 组。Cache 分 TAG SRAM 和 DATA SRAM,每行都有一个 TAG 值,假设为所存数据物理地址的高 19 位,如下图 TAG SRAM 的组织:

CORE2 可以一直读取 8 路 TAG 同时比较。INDEX 索引很显然是物理地址低位 6 Bit 截 0 (64),再取 128 的模,所以应该是 [ 12:6 ] 位为组索引,然后用要操作的地址的高 19 位与 TAG SRAM 中的 128 组中的每个值比较,一旦比较成功则 Cache Hit,这样我们便知道数据在哪组哪路。接下来便是数据的实际操作,L1 Cache DATA SRAM好像是 8 BYTE 数据读宽度,一次可以 READ 8 路。

3. 数据和指令地址对齐的重要性

现代微处理器架构在 Load 和 Store 内存时,如果访问地址为 N 的整数倍时,则可以一次操作 N 个字节 (N = 2^M),但如果操作的 N 个字节处于的地址对 N 取余不为 0,则需要 2 个或以上个时钟周期,特别是在地址跨越了 Cache Line 后,性能影响更为严重。比如读取 4 Byte 的 int 变量,如果此变量所在内存物理地址(注意是物理地址,不是线性地址)正好为 4 的整数倍,则能高效的完成操作(处于同样的 BUFFER 中时)。而且数据按其自然长度对齐还有一个好处就是将数据跨越 Cache line 的几率降到了最低。


Reference:

http://www.haifux.org/lectures/267/Introduction-to-GPUs.pdf

http://www.cnblogs.com/effulgent/archive/2009/01/14/1375845.html

https://www.cnblogs.com/effulgent/archive/2008/12/29/1364494.html

https://www.cnblogs.com/effulgent/archive/2009/03/09/1407269.html

《GPU 高性能运算之 CUDA》