概念页
GPU 并行架构
GPU Parallel Architecture
GPU 为吞吐量而设计:芯片大部分面积是成千上万个着色器核心,而不是缓存和控制逻辑。它靠在大量线程组 (warp/wavefront) 之间快速切换来隐藏访存延迟,用 SIMD 让一组线程锁步执行同一条指令。理解占用率和线程分歧,是写出高效着色器的关键。
CPU 和 GPU 的不同思路
所有处理器都要面对延迟:数据离处理器越远,等待越久,等待时处理器就停顿 (stall)。
| CPU | GPU | |
|---|---|---|
| 优化目标 | 处理各种数据结构和大型代码,降低单线程延迟 | 吞吐量:单位时间处理尽可能多的数据 |
| 芯片面积主要用于 | 快速本地缓存、控制逻辑 | 大量着色器核心 |
| 避免停顿的方式 | 分支预测、指令重排、寄存器重命名、缓存预取 | 切换到其他线程组继续工作 |
| 单个核心的延迟 | 低 | 明显更高 |
GPU 是流处理器:依次处理一批批相似的数据(顶点、像素),每次调用尽量彼此独立,不依赖邻居的信息,也不共享可写内存。
延迟隐藏:切换而不是等待
假设 2000 个片元要运行像素着色器,只有一个着色器处理器:
- 执行第 1 个片元,做几次寄存器运算,没有停顿;
- 遇到纹理读取,访存要几百到几千个时钟周期;
- 不等待,立刻切换到第 2 个片元(每个片元有自己的寄存器,切换几乎没有开销);
- 依次处理完所有片元后回到第 1 个,此时纹理数据已经返回,继续执行。
单个片元变慢了,但整体时间大幅缩短。
SIMD、线程和 warp
- 每个片元的着色器调用叫一个线程 (thread),它和 CPU 线程不同,只是一点输入内存加上寄存器空间。
- 使用同一个着色器的线程打包成组:NVIDIA 叫 warp(32 个线程),AMD 叫 wavefront。
- 一个 warp 由 8 到 64 个核心以 SIMD(单指令多数据)方式锁步执行,每个线程对应一条 SIMD 通道。这样比给每个程序配独立的控制单元省下大量芯片面积和功耗。
- 例:2000 个线程 ÷ 32 = 62.5,需要分配 63 个 warp,其中一个只有一半是满的。
- 一个 warp 遇到访存时整体换出,换入另一个 warp。换入换出不触碰线程数据,只是让核心指向另一组线程,所以几乎零开销。warp 切换是所有 GPU 隐藏延迟的主要机制。
下图是 warp 切换的简化过程:每个 warp 执行到纹理读取就换出,等其他 warp 也停顿后再换回来。
sequenceDiagram
participant Core as 着色器核心
participant W1 as Warp 1
participant W2 as Warp 2
participant W3 as Warp 3
Core->>W1: 执行 mad、mul
W1-->>Core: txr 纹理读取,停顿
Core->>W2: 换入,执行 mad、mul
W2-->>Core: txr,停顿
Core->>W3: 换入,执行 mad、mul
W3-->>Core: txr,停顿
Core->>W1: 换回,纹理已返回,继续 cmp、emt占用率 (Occupancy)
- 驻留在 GPU 上的 warp 叫"在飞行中 (in flight)",它们的数量就是占用率。
- 每个线程用的寄存器越多,能同时驻留的线程和 warp 就越少;着色器使用的共享内存也会影响占用率。
- 占用率低时,停顿无法靠切换掩盖,性能往往很差;线程太少时也建不出足够的 warp。
- 访存越频繁,需要的延迟隐藏越多。不过理想的占用率会随着色器的运算类型变化,不一定越高越好。
线程分歧 (Thread Divergence)
- 着色器里的 if 和循环会造成动态分支。
- 如果一个 warp 里所有线程都走同一个分支,没有问题。
- 只要有一个线程走了另一条路,整个 warp 就要两条分支都执行,再丢掉每个线程不需要的结果,其他线程在这段时间里闲着。
在游戏开发中的意义
- 优化着色器时,减少寄存器占用和访存次数,往往比减少算术指令更重要。
- 尽量让分支条件在一个 warp 内一致,比如按材质或类型分批绘制;依赖逐像素数据、变化剧烈的分支代价很高。
- 理解了这套机制,才能看懂 GPU 性能分析工具里的占用率、停顿原因等指标。
GEA 的补充(卷 I 4.11)
- GEA 以 AMD GCN 为例说明 wavefront 为什么是 64:计算单元 (CU) 里的 SIMD 单元只有 16 通道宽,但内部是 4 级流水线、一条指令要 4 个周期,CU 对四组 16 个元素重复同一指令,流水线才不空闲,所以一个 wavefront(NVIDIA 称 warp)是 64 个元素。CU 不做推测执行和乱序执行。
- GEA 解释零开销切换的硬件原因:每个 SIMD 单元的物理寄存器约为单个 wavefront 可用逻辑寄存器的十倍,最多约十个 wavefront 的寄存器同时常驻,切换时无需保存寄存器。这与本页「寄存器用得越多,驻留的 warp 越少」的占用率结论是同一件事的两面。
- 术语:GEA 明确说「GPU 线程」就是一条 SIMD 通道;本页(RTR4)说一个 warp 由 8 到 64 个核心锁步执行、每个线程对应一条 SIMD 通道。两者一致,只是 GEA 把通道宽度(16)与 wavefront 大小(64)区分得更清楚。详见 GPGPU 编程入门。
延伸阅读
- Giesen《A Trip through the Graphics Pipeline》
- Fatahalian 和 Bryant 关于 GPU 并行的课程讲义
出处
- realtime_rendering_4ed.pdf 第 3 章 The Graphics Processing Unit
- Game_Engine_Architecture_4Ed_-_Jason_Gregory.pdf
- Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 I 第 4 章 4.1.4.2、4.11 节
相关页面
图形学可编程着色器与 GPU 管线Programmable Shaders & GPU PipelineGPU 把第 2 章的概念管线实现为一组硬件阶段,分为完全可编程、可配置但不可编程、完全固定三类。现代 GPU 采用统一着色器架构:各类着色器共用同一套编程模型和核心,由 GPU 自行分配负载。着色器用 HLSL、GLSL 等类 C 语言编写,输入分为 uniform 和 varying 两类。图形学像素着色器Pixel Shader像素着色器(OpenGL 里叫片元着色器)对光栅化产生的每个片元运行,用插值后的顶点数据算出颜色,也可以输出不透明度、修改深度或直接丢弃片元。多渲染目标让它一次输出多组数据,催生了延迟着色。它通常只能写自己所在的像素,但能通过 2×2 quad 拿到相邻像素的导数。图形学计算着色器Compute Shader计算着色器是 DirectX 11 引入的一种 GPU 计算方式:它不固定在图形管线的某个位置,但由图形 API 调用,和其他着色器共用统一着色器核心。它暴露了线程组和组内共享内存,最大的优势是能直接处理 GPU 上生成的数据,不用来回传给 CPU。图形学动态漫反射全局光照Dynamic Diffuse Global Illumination烘焙动辄数小时,拖慢美术迭代,几何在运行时变化或由用户创建时甚至无法预计算。动态漫反射 GI 方法无需预处理或预处理快到每帧可做,主要有:基于虚拟点光源的反射阴影贴图 (RSM)、光传播体积 (LPV)、体素锥追踪 (VXGI) 以及屏幕空间补充(RTR4 11.5.5–11.5.9 节)。图形学渲染瓶颈定位与性能度量Bottleneck Location & Performance Measurement渲染管线中最慢的阶段(瓶颈)决定整体吞吐量。优化就是"找到瓶颈 → 优化到瓶颈转移 → 重复",并让非瓶颈阶段做到与瓶颈同样多的工作以换取画质。定位靠逐阶段增减负载的实验和性能工具;度量要用毫秒帧时间而不是 FPS。两条箴言:了解你的架构 (KNOW YOUR ARCHITECTURE);测量、测量、再测量。图形学像素与帧缓冲优化Pixel & Framebuffer Optimization需要着色的像素通常远多于顶点,所以像素处理优化往往最划算。核心手段:保证占用率以隐藏纹理延迟、按距离切换着色器 LOD、利用 early-z 并通过从前往后排序或 z-prepass 减少过度绘制、缩小帧缓冲每像素存储(色度子采样、YCoCg 棋盘),以及只在需要时开启混合、避免 GPU 回读。几何与光栅化阶段的常用优化也一并列出。图形学分块着色Tiled Shading分块着色把屏幕划分为瓦片(如 32×32 像素),先为每个瓦片建立可能影响它的光源列表,着色时每个像素在一次着色器调用中只遍历所在瓦片的列表。它可用于延迟着色(tiled deferred),也可用于前向着色(tiled forward,又称 Forward+)。配合 z-prepass 得到的瓦片深度范围可进一步剔除光源,但深度不连续会削弱效果,于是出现 HalfZ、2.5D 剔除等改进。图形学GPU 计算调度与占用率GPU Compute Scheduling & OccupancyGPU 用大量统一的 ALU 以 SIMD/SIMT 方式锁步执行着色程序,ALU 与寄存器文件、L1、本地数据存储、纹理单元等组成多处理器 (MP),由硬件 warp 调度器零开销地切换 warp 以隐藏纹理和内存访问的长延迟。能同时驻留的 warp 数受寄存器等资源限制,用占用率衡量;占用率低会妨碍延迟隐藏,但过高也可能抖动缓存。本页还讨论像素按 quad 调度和 GPU→CPU 回读延迟。图形学并行图形架构与排序分类Parallel Graphics Architectures & Sort Taxonomy提升图形速度的最佳方式是并行:多个几何单元、光栅化单元和像素处理单元同时工作再合并结果。Amdahl 定律指出串行部分限制总加速比。由于 API 要求按提交顺序处理图元,并行单元之间必须在某处做"模型空间到屏幕空间"的排序,按排序位置分为 sort-first、sort-middle、sort-last fragment、sort-last image 四类,各有负载均衡与带宽取舍。本页还简述光线追踪硬件的共同特征。游戏开发并发与并行基础Concurrency & Parallelism FundamentalsGEA 区分两个正交的概念:并发 (Concurrency) 是软件层面多个控制流读写共享数据,核心难题是数据竞争;并行 (Parallelism) 是硬件层面两个以上部件同时工作。硬件并行又分为提升单线程速度的隐式并行(流水线、超标量、VLIW,即指令级并行 ILP)和为同时运行多条指令流而设计的显式并行(超线程、多核、多处理器、集群/云)。图形学GPGPU 编程入门Introduction to GPGPU ProgrammingGEA 把 GPGPU 讲成「SIMD 向量化推到极致」:GPU 结合 SIMD(向量化 ALU)与 MIMD(一种抢占式多线程),即 NVIDIA 所说的 SIMT。适合 GPU 的任务必须是数据并行的——每个元素的计算不依赖其他元素的结果。写法与 CPU 向量化相反:不把循环硬编码成每次 4/8/16 个,而是保留单通道的循环体,抽成计算核函数 (compute kernel),由 GPU 替我们「跑循环」。一个「GPU 线程」其实就是一条 SIMD 通道。图形学渲染管线Rendering Pipeline渲染管线是实时图形的核心:给定虚拟相机、三维物体、光源等,把场景转换成一张二维图像。RTR4 把它粗分为四个功能阶段:应用、几何处理、光栅化、像素处理,各阶段并行执行,整体速度由最慢的阶段决定。图形学实时渲染Real-Time Rendering实时渲染是在计算机上快速生成图像、并让图像随用户输入即时变化的技术。RTR4 认为它由三个要素共同定义:交互性、三维空间感,以及图形加速硬件 (GPU)。图形学波前路径追踪Wavefront Path Tracing波前 (wavefront) 架构把路径追踪拆成一串独立的 GPU 内核(生成相机光线、采样、求交、介质采样、逃逸光线、命中发光体、按材质分开的着色、阴影光线、写胶片),内核之间用工作队列传递数据;每个内核开始时线程都在做同一件事,执行是收敛的。代价是每个内核都要从显存读入、再写回全部状态,因此更受带宽限制。pbrt 4e 的 GPU 渲染路径 WavefrontPathIntegrator 就是这种结构,算法与 CPU 上的 VolPathIntegrator 完全相同(见 参与介质与体积散射),只改变计算的组织方式和数据布局。注意术语:这里的"波前"指整批光线按深度推进的处理方式,与 AM图形学片元着色器与 GLSL 基础Fragment Shaders & GLSL Basics片元着色器(fragment shader)是在每个像素(片元)上并行执行的小程序:输入位置等信息,输出颜色。GLSL 是 OpenGL / OpenGL ES / WebGL 共用的着色语言;本书用全屏 billboard 上的像素着色练习,建立程序化图形的基础心智模型。