[toc]

最近在学习 GPGPU 相关的知识,开这个系列对学习的知识进行总结

# GPU 是什么

GPU 全称是 Graphics Processing Unit,图形处理单元。它的功能最初与名字一致,是专门用于绘制图像和处理图元数据的特定芯片,后来渐渐加入了其它很多功能。

GPU 架构发展史 (NVIDIA)

大体而言: GPU 在早期是为了优化 3D 渲染管线而诞生的一种架构,其主要的目标就是对大批量的矩阵运算与数值运算进行高并发的处理。
 在架构演化中,人们发现 GPU 的性能不应该值限制在 3D 渲染这一个领域内,同时,借助人工智能中神经网络的兴起,GPU 的架构也从早期的固定流水线逐渐的转换到现如今的通用计算单元的架构模式,这一转换,使得 GPU 从单一的图形处理变成了可以应对超高并发计算的硬件单元。
 2007 年,CUDA 诞生,2008 年,NVIDIA 第一款统一管理计算单元的 GPU 处理器架构诞生。

  • 2008 - Tesla : 首款统一了渲染管线中顶点 / 片元计算单元的处理器
  • 2010 - Fermi
  • 2012 - Kepler
  • 2014 - Maxwell
  • 2016 - Pascal
  • 2017 - Volta
  • 2018 - Turing

计算能力的变迁 (GFLOAT/s 每秒可以计算多少 G 的浮点运算)

# CPU 与 GPU 的区别

  1. CPU 是一种通用的处理器架构,而 GPU 则专注于超高并发的计算
  2. (举个栗子) CPU 的核心数量四核,GTX3060 GPU 核心数量 3584, 这里的核心可以理解为并发数量
  3. CPU 会高效的处理程序中出现分支的情况,GPU 多一层分支可能多一个幂次的消耗
  4. CPU 单指令最多执行 4 次运算 (SIMD),GPU 单次指令可以执行非常多的运算 (SIMT)

# 概念词语整理

在进行下一步之前,现将所有可能用到的概念性词语整理在一起,方便后续遇到时查阅,可以对照着下图进行一一对应

名词

英文

中文

GPC

Graphics Processing Cluster

图形处理簇 (簇在计算机科学中通常可以理解为块 / 一起)

TPC

Texture/Processor Cluster

纹理处理簇

Thread

Thread

线程

SM

Stream Multiprocessor

流多处理器 (Tesla 架构下是包含在一组 TPC 中,即一组 TPC 内有多组 SM, 每个 SM 包含多个流处理器,所以叫流多), 但是 SM 的架构包括下述架构其实在后续迭代中都是一直变更的,这个表格单独记录的是 Tesla 架构

SP

Streaming Processor

流处理器 (最基础的处理单元)

SMX

SM 的改进

SMM

SM 的改进

Warp

Warp

多个 SM 单元组成的线程束 (组)

ALU

Arithmetic And Logic Unit)

算数逻辑单元 (每个 SM 中有 1 个,包含在 SM 中,而非 SP)

FPU

Float Processor Unit

浮点数运算单元 (每个 SM 中有 1 个,包含在 SM 中,而非 SP)

SFU

Special function units

特殊数学运算单元 (sin/cos/log 等,每个 SM 中有多个)

ROP

render output unit

渲染输入单元 (像素着色器后)

Load/Store Unit

L1/2 Cache

SM->L1->L2->GPU Memory->System Memory 速度逐层递减

Memory

= =

= =

Register File

Register File

寄存器,SM 本身包含的

SIMT

Single Instruction Multiple Thread

单指令多线程

# 层级理解

以下均是我的个人理解,因为 GPU 相对于 CPU 而言,少了很多复杂的逻辑运算控制,而更多的是专注于运算单元的数量,所以至少从现在的 GPU 架构上来看,还是具备一种比较清晰的层级架构的 GPGPU层级架构

  • 一个 GPU 由多个 Graphics Process Cluster 组成
  • 一个 Graphis Process Cluster 由多个 Texture Process Cluster 组成
  • 一个 Texture Process Cluster 由多个 Stream Multiprocessor (SM) 组成
  • 一个 Stream Multiprocessor 由多个 Stream Processor (SP) 组成

另外,

  • ALU/SPU 等运算单元是在 SM 内存放的,而非 SP (所以多个 SP 可能会出现资源抢占的问题)
  • CUDA 中的 Thread 对应的是一个 SP (可以理解为 Threa 就是 SP?)
  • CUDA 中的 Wrap 对应的是一组线程束 (即多个 SP 的组合), 一个 SM 中可以运行多个 Wrap

# 为什么 GPU 有远高于 CPU 的性能?

GPU 其实是专注于计算的一种硬件设备,而且在人工智能不断发展的今天,同一个 GPU 硬件上可能还会携带不同种类的特殊计算单元 (SFU), 如专用于计算 FFT 的计算单元
  相对的,GPU 对于功能性的处理就即为弱势,如分支预测、条件转移、循环、逻辑判断、子程序调用等,在 GPGPU 代码中使用 if, 最坏的情况下,写多少层 if,就多 2^n 幂的消耗
  回到题目: 为什么 GPU 有远高于 CPU 的性能?,通常,对于一个 GPU 处理单元 (核 / 同时是上面所说的 SP) 而言,会比 CPU 上的核心慢 N (有印象是 4—8) 倍,但是 GPU 由于抛弃了复杂的通用性,而专注于运算,所以其核心数量远高于 CPU 的核心数量 (GTX3060 是 3584 个处理核心,而 CPU 可能只有 4~32 核)。
  简单来说,很粗暴的数量压制   另外,通常 CPU 端会使用 SIMD 技术对四次运算进行单次指令的合并,而 GPU 端则有 SIMT 技术 (Single Instruction Multiple Thread、单指令多线程)
  SIMD 是在 ALU 硬件具备单指令计算四个浮点数的运算前提下实现的
  SIMT 是在 GPU 中的每个 SM 中组成

# 为什么说 GPU 的分支运算性能极低

 如上图,一个线程束内,可能有些线程走 if, 有些线程走 else,这就导致,在离开 if 前,需要将所有的线程同步到 if/esle 的运算结束,GPU 实现的很简单,就是等所有 If/Else 运算都结束了才允许进行下一步,所以,如果分支预测错误,就必然会导致所有线程消耗了执行完完整的 if/else 两个分支的时间
 可以想象,如果 if 嵌套层数过多会导致什么情况 (掀桌)

# 附:稀疏数据结构优化

可以看到,无论是 GPU 还是 CPU 都有 L1/L2 Cache, 而不同层级的 Cache 访问次数对于性能的影响还是比较大的,《GPGPU 编程模型与架构原理》这本书中有说:
 L1 访问延时: 0.5ns
 L2 访问延时: 7ns (14 倍 L1)
 主内存访问延时: 100ns (200 倍 L1)  更不要提 CPU 与 GPU 之间的访问延时了,至少在图形学编程中,一般都是要极力避免频繁的 CPU-GPU 数据拷贝的  Fermi 架构下的 L1 Cache 内存大小是 64KB,
 所以,我们如何能够减少 L1/L2 / 主存之间的内存交换呢?
 这就引出了, 稀疏数据结构
 在学生时期,我们有学到链表等数据结构,如果不采用密集的存储方式的话,很可能导致上一个节点到下一个节点并非是连续的,这就导致,处理器检查 L1/L2/L3 cache 时,很有可能发现下一个节点的数据不在缓存中,就会触发 Cache Miss, 过于离散的数据结构通常会带来非常频繁的 Cache miss
 解决这个问题的方式就是尽量保持数据结构的紧密性,如使用一维矩阵模拟高维矩阵,如电脑中存储图片通常也是一个线性数组,这就是稀疏数据结构。  之前有看太极语言创世人胡渊鸣的一个演讲:
MIT 在读博士生胡渊鸣:Taichi 编程语言 — 高性能稀疏视觉计算与可微编程  大概讲的就是他们的这个框架处理稀疏数据结构非常简单 (最近看了之后就开始关注稀疏数据结构这个课题了) 其实稀疏数据结构是用来处理稀疏数据的稠密结构  还有他们的官网也有对稀疏数据结构的一个文档: Taichi Lang - 空间稀疏数据结构

# 参考文章

深入 GPU 硬件架构及运行机制 《通用图形处理器设计 - GPGPU 编程模型与架构原理》 CUDA 开启 GPU 编程 - 小彭老师