> 本文是《深入 NVIDIA GPU:从图形流水线、SM 微架构到 AI 超级芯片》专栏的第 1 章。
> 本章从计算机图形系统的实际需求出发,解释 GPU 为什么出现,以及它为什么从一开始就选择了不同于 CPU 的硬件设计路线。
摘要
今天提到 GPU,人们往往首先想到 CUDA、深度学习、Tensor Core 或大模型训练。但 GPU 最初并不是为通用计算设计的,它的直接目标是解决一个非常具体的问题:**如何持续、快速地生成屏幕上的像素。**
早期计算机主要依靠 CPU 完成图形计算。随着分辨率、色彩深度、三维场景复杂度和实时帧率不断提高,CPU 很难同时承担操作系统、应用逻辑和大量重复的图形运算。于是,一部分显示工作被逐步转移到专用硬件:显示控制器、二维加速器、三维光栅化芯片,最终发展成集几何处理、光栅化、纹理映射和像素输出于一体的图形处理器。
GPU 的核心价值并不是"单个运算单元比 CPU 更强",而是用大量相对简单的运算资源,同时处理海量结构相似的数据,以获得极高的总体吞吐率。
一、本章要解决的问题
读完本章,应当能够回答以下问题:
-
在 GPU 出现之前,计算机如何生成画面?
-
为什么图形任务会逐渐成为 CPU 的负担?
-
显示控制器、图形加速器和 GPU 有什么区别?
-
为什么图形处理天然适合并行硬件?
-
GPU 与 CPU 的设计目标为什么不同?
-
NVIDIA GeForce 256 在 GPU 发展史上有什么意义?
二、从一块屏幕说起
屏幕可以抽象为一个二维像素阵列。假设分辨率为 1920×1080,每个像素使用 32 bit 表示颜色,那么保存一帧画面大约需要:
```text
1920 × 1080 × 4 Byte ≈ 7.91 MiB
```
如果屏幕每秒刷新 60 次,仅仅把完整帧缓冲区读取并送往显示设备,理论数据量就约为:
```text
7.91 MiB × 60 ≈ 474.6 MiB/s
```
这里还没有计算:
-
三角形的坐标变换;
-
光照计算;
-
纹理读取和过滤;
-
深度测试;
-
透明度混合;
-
抗锯齿;
-
后处理;
-
多次中间渲染。
真正的三维渲染需要先经过大量计算,才能得到最终的一帧像素。
因此,图形系统面对的并不只是"把一张图片显示出来",而是要在很短的时间内完成大量几何计算、采样、比较、插值和像素写入。
三、GPU 出现之前:CPU 加帧缓冲
早期图形系统可以简化为以下结构:
```mermaid
flowchart LR
A"应用程序" --> B"CPU"
B --> C"系统内存中的图像数据"
C --> D"帧缓冲区"
D --> E"显示控制器"
E --> F"显示器"
```
其中各部分的工作如下:
-
CPU 计算要显示的内容;
-
帧缓冲区保存每个像素的颜色;
-
显示控制器按照固定时序读取帧缓冲;
-
数字或模拟显示接口把图像信号送到显示器。
此时的显示控制器主要负责"扫描输出",并不负责复杂的图形生成。画线、填充矩形、复制图像,甚至字体绘制,都可能需要 CPU 逐像素完成。
这种方式在低分辨率、低刷新率和简单界面下可以工作,但存在明显问题:
-
CPU 需要执行大量重复操作;
-
图像数据占用系统内存带宽;
-
CPU 在处理图形时无法及时处理其他任务;
-
分辨率和色彩深度提高后,数据量迅速增长。
这推动了第一类图形专用硬件的出现。
四、从显示控制器到二维图形加速器
二维图形界面中经常出现一些高度重复的操作:
-
绘制直线;
-
填充矩形;
-
位块传输,即 BitBLT;
-
图像复制;
-
颜色填充;
-
光标叠加;
-
字体和窗口绘制。
这些操作规则明确,而且会反复执行,非常适合固化为专用硬件。
例如,CPU 不再逐像素执行矩形填充,而是向图形加速器写入:
```text
起点坐标 = (x, y)
宽度 = width
高度 = height
颜色 = color
操作 = FILL_RECT
```
图形加速器收到命令后,自行生成目标地址并完成批量写入。
```mermaid
flowchart LR
A"CPU 提交绘图命令" --> B"2D 图形加速器"
B --> C"地址生成"
B --> D"颜色填充 / BitBLT"
C --> E"显存"
D --> E
E --> F"显示控制器"
```
这样做有三个直接收益:
-
CPU 只提交命令,不再处理每个像素;
-
专用电路可以用更少的周期完成固定操作;
-
图形数据可以放在独立显存中,减轻系统内存压力。
不过,二维加速器仍然无法解决日益增长的三维图形计算问题。
五、三维图形流水线为什么更加复杂
三维场景通常由大量顶点和三角形组成。要把三维场景显示在二维屏幕上,大致需要以下步骤:
```mermaid
flowchart LR
A"模型顶点" --> B"模型 / 视图 / 投影变换"
B --> C"光照与裁剪"
C --> D"透视除法与视口变换"
D --> E"三角形建立"
E --> F"光栅化"
F --> G"纹理采样与片元处理"
G --> H"深度 / 模板测试"
H --> I"颜色混合"
I --> J"帧缓冲区"
```
不同阶段处理的数据类型不同:
| 阶段 | 主要输入 | 典型计算 |
|---|---|---|
| 几何处理 | 顶点 | 矩阵乘法、向量运算 |
| 三角形建立 | 顶点属性 | 边界计算、裁剪 |
| 光栅化 | 三角形 | 覆盖测试、属性插值 |
| 纹理处理 | 坐标和纹理 | 寻址、采样、过滤 |
| 像素处理 | 片元 | 颜色计算、深度比较 |
| 输出合并 | 像素 | 混合、写回 |
这些计算有一个共同特征:**同一种运算会作用于大量顶点、片元或像素。**
例如,模型中的每个顶点都要乘以变换矩阵;一个三角形覆盖的许多像素都要进行纹理采样;屏幕上大量片元都要执行深度测试。不同数据之间往往具有较高的独立性,因此可以并行处理。
六、为什么图形处理天然适合并行
假设需要把同一个亮度调整公式应用到一百万个像素:
```text
outputi = clamp(inputi × scale + bias)
```
传统串行思路是一个接一个地处理:
```text
像素0 → 像素1 → 像素2 → ...... → 像素999999
```
并行思路则是让多个运算单元同时处理不同像素:
```text
运算单元0 → 像素0
运算单元1 → 像素1
运算单元2 → 像素2
......
```
只要像素之间没有强依赖,就不必等待前一个像素完成。
三维图形拥有大量这种数据级并行:
-
不同顶点可并行变换;
-
不同三角形可并行建立;
-
不同片元可并行着色;
-
不同像素可并行测试和写回。
图形工作负载还具有较好的规律性。许多数据执行相同或相近的操作,硬件可以让多个执行通道共享控制逻辑,从而把更多晶体管用于计算,而不是复杂的指令调度和分支预测。
这正是 GPU 选择高吞吐架构的重要原因。
七、CPU 与 GPU 的目标并不相同
CPU 和 GPU 都能执行计算,但二者优化的目标不同。
| 对比项目 | CPU | GPU |
|---|---|---|
| 主要目标 | 降低单线程延迟 | 提高总体吞吐率 |
| 任务特征 | 复杂控制、串行逻辑 | 大规模相似计算 |
| 核心数量 | 较少、单核复杂 | 大量、单元相对简单 |
| 缓存 | 大容量、多级低延迟缓存 | 用缓存与大量线程共同隐藏延迟 |
| 控制逻辑 | 分支预测、乱序执行、推测执行 | 更强调批量调度和规则执行 |
| 并行方式 | 线程级并行、SIMD | 大规模线程与 SIMT |
| 延迟处理 | 尽量缩短一次操作的等待 | 某批线程等待时执行其他线程 |
可以用两种交通工具来类比:
-
CPU 像少量高性能轿车,擅长尽快把少数乘客送到目的地;
-
GPU 像拥有大量座位的列车系统,追求单位时间运输更多乘客。
这个类比并不完全精确,但能说明"延迟"和"吞吐率"的区别。
NVIDIA 的 CUDA 最佳实践文档也将二者概括为:CPU Core 主要面向少量线程的低延迟执行,GPU 则面向大量轻量线程以最大化吞吐率。
需要注意,GPU 不是在所有任务上都比 CPU 快。当任务存在以下特征时,GPU 的优势可能很小:
-
串行依赖很强;
-
分支路径高度不规则;
-
数据规模很小;
-
CPU 与 GPU 之间的数据传输成本过高;
-
并行计算量不足以覆盖调度和启动开销。
八、固定功能三维加速器
在早期三维图形硬件中,许多阶段被直接实现为固定功能电路:
```mermaid
flowchart LR
A"CPU 处理顶点或提交命令" --> B"固定功能几何阶段"
B --> C"固定功能光栅化"
C --> D"固定功能纹理映射"
D --> E"固定功能颜色混合"
E --> F"显存"
```
所谓固定功能,是指硬件支持预先规定好的算法和状态组合。开发者可以设置参数,却不能任意改写处理逻辑。
其优点包括:
-
电路针对特定操作优化;
-
性能高;
-
功耗和面积效率好;
-
行为相对确定。
缺点则是:
-
难以实现新的光照和材质算法;
-
硬件功能升级依赖新芯片;
-
不同效果常常需要多遍渲染;
-
灵活性越来越无法满足游戏和专业图形需求。
因此,固定功能流水线只是 GPU 发展的一个阶段。后来出现的可编程着色器,将逐步改变图形处理器的性质。
九、GeForce 256 与"GPU"概念
1999 年,NVIDIA 发布 GeForce 256,并将其称为 GPU。NVIDIA 当时强调,它在单芯片中集成了变换、光照、三角形建立/裁剪和渲染等功能。NVIDIA 的公司时间线将 GeForce 256 称为其首款 GPU。
GeForce 256 的重要性不只是产品名称,而是它体现了一次明确的职责转移:
```text
早期:
CPU 完成大量几何计算
图形芯片主要负责光栅化和输出
GeForce 256 所代表的方向:
更多几何处理进入图形芯片
图形流水线在专用处理器内部变得更加完整
CPU 更专注于应用逻辑和命令提交
```
需要保持一个严谨认识:
> 图形加速硬件在 GeForce 256 之前已经存在多年,"GPU"也不是某一天凭空出现的全新物种。
GeForce 256 更适合作为一个具有代表性的里程碑:NVIDIA 将 GPU 一词系统地用于产品定位,并把硬件变换与光照等阶段集成到消费级单芯片图形处理器中。
换句话说,GPU 的形成是一个连续演进过程:
```mermaid
flowchart LR
A"显示控制器" --> B"2D 图形加速器"
B --> C"3D 光栅化加速器"
C --> D"集成几何处理的 GPU"
D --> E"可编程 Shader GPU"
E --> F"统一着色器 GPU"
F --> G"通用并行计算 GPU"
G --> H"AI / HPC 加速平台"
```
十、GPU 硬件设计的基本思想
回顾 GPU 的形成过程,可以提炼出几条贯穿后续架构演进的设计思想。
1. 把重复工作从 CPU 卸载到专用硬件
最早是扫描输出,随后是二维绘图、光栅化、纹理处理、几何变换,后来又扩展到光线追踪和矩阵运算。
2. 用并行资源换取吞吐率
当大量数据能够执行相似操作时,复制运算通道比增强单个复杂核心更有效。
3. 让控制成本由一组数据共同分担
多个数据元素执行相同指令时,可以共享部分取指、译码和调度逻辑。这一思想后来发展为 NVIDIA 的 SIMT 执行模型。
4. 通过流水线让不同任务重叠执行
当一个三角形进行光栅化时,后续顶点可以进行变换,之前生成的片元也可以进入深度测试。多个阶段同时工作,提高了硬件利用率。
5. 专用化与可编程性不断摆动
完全固定功能的硬件效率高但不灵活;完全通用的硬件灵活但成本较高。GPU 的发展并不是简单地取消专用单元,而是在可编程核心与纹理、光栅、视频、光追、矩阵等专用单元之间寻找平衡。
十一、一个完整的早期三维渲染流程
以显示一个旋转的三角形为例:
-
应用程序在 CPU 上更新三角形的旋转角度;
-
CPU 准备顶点、颜色和纹理等数据;
-
CPU 向图形处理器提交绘制命令;
-
几何阶段把三维顶点变换到屏幕坐标;
-
三角形建立单元确定三条边和覆盖范围;
-
光栅化器生成三角形覆盖的片元;
-
纹理单元根据插值后的坐标读取纹理;
-
深度测试判断片元是否被其他物体遮挡;
-
通过测试的颜色被写入帧缓冲;
-
显示控制器扫描帧缓冲并输出到显示器。
从这个流程可以看到,CPU 负责"决定画什么",GPU 负责"把它高效地画出来"。
现代 GPU 的执行过程已经复杂得多,但这一异构分工至今仍然存在:CPU 处理复杂控制和系统任务,GPU 处理适合大规模并行的工作。
十二、常见误区
误区一:GPU 就是显卡
GPU 是处理器芯片或其中的图形/计算核心;显卡则是一套完整板级产品,通常包含 GPU、显存、供电、PCB、散热器和外部接口。
误区二:GPU 从诞生之初就是通用处理器
早期 GPU 主要围绕图形流水线设计,通用可编程能力是逐步发展出来的。
误区三:GPU 的每个核心相当于一个 CPU Core
"CUDA Core"通常指 SM 中的算术执行通道,与具有复杂控制、缓存和乱序执行能力的 CPU Core 不是同一层级的概念。
误区四:GPU 比 CPU 快,所以可以取代 CPU
GPU 擅长高并行、高吞吐任务;CPU 擅长复杂控制、低延迟响应和串行任务。现代系统通常让二者协同工作。
误区五:并行单元越多,程序就一定越快
实际性能还受并行度、内存带宽、数据依赖、分支、调度、数据传输和软件实现影响。
十三、本章总结
GPU 并不是从通用计算需求中诞生的,而是图形系统不断发展的结果。
它经历了以下演进:
-
显示控制器负责读取帧缓冲和生成显示信号;
-
二维加速器接管画线、填充和 BitBLT;
-
三维加速器接管光栅化、纹理和像素处理;
-
几何变换与光照进入单芯片图形处理器;
-
GPU 逐渐形成完整的硬件图形流水线。
GPU 之所以采用大规模并行架构,是因为顶点、三角形、片元和像素处理中存在大量结构相似、相对独立的计算。它通过牺牲一部分单线程控制能力,换取更高的整体吞吐率。
但此时仍然存在一个核心问题:固定功能硬件只能执行预先设计好的算法。开发者如果想实现新的材质、光照和视觉效果,就需要更强的可编程能力。
这正是下一章要讨论的内容。
十四、思考题
-
为什么提高屏幕分辨率会同时增加计算压力和显存带宽压力?
-
BitBLT 为什么适合由专用硬件实现?
-
为什么三角形比任意复杂多边形更适合作为硬件光栅化的基本图元?
-
固定功能电路和可编程处理器分别有什么优势?
-
GPU 为什么选择提高吞吐率,而不是重点降低单线程延迟?
-
GPU 是否只适合图像数据?决定一个问题是否适合 GPU 的真正因素是什么?
十五、参考资料
-
NVIDIA Corporate Timeline:1999 年 GeForce 256(https://www.nvidia.com/content/timeline/time_99.html)
-
NVIDIA CUDA Programming Guide(https://docs.nvidia.com/cuda/cuda-programming-guide/part1.html)
-
NVIDIA CUDA C++ Best Practices Guide(https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html)
-
Khronos OpenGL Wiki:History of Programmability(https://wikis.khronos.org/opengl/History_of_Programmability)
-
Khronos OpenGL Wiki:History of OpenGL(https://wikis.khronos.org/opengl/History_of_OpenGL)
下一章预告
下一章为:
> **《【深入 NVIDIA GPU 架构 02】从固定功能流水线到可编程着色器》**
下一章将重点讨论:
-
固定功能图形流水线的限制;
-
Vertex Shader 与 Pixel/Fragment Shader 为什么出现;
-
Shader 指令、寄存器和执行单元;
-
DirectX 与 OpenGL 如何推动 GPU 可编程化;
-
可编程着色器如何为后来的统一着色器架构铺路。