开篇定下了方向:从 architecture-first 而非 feature-first 的视角看 GPU。这一篇从发展历史、Graphics API、Graphics Driver 到宏观与微观架构,搭一个跨厂商的 GPU 分析框架。面向具备计算机体系结构基础但尚未接触 GPU 领域的工程师,目标是建立一套可重复的分析方法:从 Feature / API Abstraction 出发,追踪到 Physical Structure、Data Movement、Dependency Tracking、Pipeline Pressure、Observable Stall 与 Programming Consequence。
记住模块列表换不来对 GPU 的理解。要点是从 Feature 追到硬件路径,把这条追踪变成分析习惯。
一、发展历史
GPU 架构的每一次转折,都是 data movement、dependency tracking、fixed-function ownership、programmability 四项要素在硬件与软件之间的重新分配。一个现代 Vulkan Pipeline Barrier 需要填写 srcStageMask、dstStageMask、srcAccessMask、dstAccessMask、oldLayout、newLayout 一整组字段,每个字段背后是一次硬件层面的缓存清洗与可见性切换。这套复杂度从何而来、是否会一直存在,答案在架构演进本身。
以下按 Fixed Function → Programmable Shader → Unified Shader Architecture → GPGPU → Specialized Acceleration Units 的链条展开。每个阶段都围绕五个问题组织:硬件路径改变了什么?data movement 改变了什么?dependency tracking 改变了什么?开发者获得了什么控制权?新增了什么失败模式?
1.1 Fixed Function
二十世纪九十年代,实时三维图形从学术领域进入消费级市场。CPU 处理几何变换、光栅化、纹理映射等高度规则的重复计算时效率不足,专用图形加速硬件随之出现。早期 GPU(3dfx Voodoo 系列、NVIDIA Riva TNT 系列、ATI Rage 系列)将整条渲染管线硬编码为固定功能电路,依次执行几何变换(Geometric Transformation)、光照计算(Lighting Calculation)、三角形设置(Triangle Setup)、光栅化(Rasterization)、纹理映射(Texture Mapping)、深度缓冲测试(Z-buffering)与颜色混合(Color Blending)。
硬件路径特征
Fixed Function 管线的每个阶段都由专用电路实现,数据在阶段之间沿固定物理路径流动。硬件逻辑完全预先设计并固化,开发者无法更改各阶段的内部计算行为。工程目标是将图形处理从 CPU 中解耦,以专用电路的吞吐效率弥补通用处理器的不足。
Data Movement 特征
Fixed Function 时代的 data movement 高度结构化且单向。顶点数据从 Host Memory 经总线进入 GPU,依次流经几何阶段、光栅化阶段、像素阶段,最终输出到 framebuffer。每一步的数据流向由硬件硬编码决定,开发者无法干预中间数据的驻留位置或传输路径。纹理数据从 Device Memory 经专用 Texture / Sampler Path 读取,同样不受开发者控制。整个 data movement 模型可概括为"单向流水线 + 固定节点缓存",没有可编程的内存访问能力。
Dependency Tracking 特征
Fixed Function 硬件内部存在严格的流水线依赖,但这些依赖完全由硬件管理,对开发者不可见。几何变换必须在光栅化之前完成,像素着色必须在深度测试之前完成,这些时序约束固化在硬件电路中。Dependency tracking 以简单的流水线 stall / bubble 形式实现,没有暴露给软件层的控制接口。开发者既不需要也不能管理阶段之间的依赖关系。
开发者控制权
开发者控制权很有限。API 主要是状态设置和数据提交的接口:指定顶点属性(位置、颜色、纹理坐标)、选择渲染状态(混合模式、深度测试开关等)、提交绘制调用。开发者不编写在 GPU 上执行的程序,只能通过配置硬件状态来影响输出。Fixed Function GPU 的定位更接近一个高度专门化的状态机,而非可编程处理器。
新增的失败模式
Fixed Function 设计引入的失败模式主要是灵活性缺失。硬件逻辑一旦固化,就无法支持新的图形算法或视觉风格。当图形学理论进步(如逐像素光照、自定义材质模型)需要突破固定管线的计算模式时,Fixed Function 硬件无法适应。固定管线的 stage-to-stage data movement 也缺乏优化空间:即使某个应用不需要某些中间计算步骤,数据仍必须流经完整的硬件链路,造成不必要的带宽与延迟开销。
对 Graphics API 的影响
Fixed Function 阶段的 API(早期 OpenGL、Direct3D、3dfx Glide)设计成状态机模型。开发者通过设置状态参数控制渲染流程,API 调用直接映射到硬件寄存器配置。这种 API 设计反过来强化了 Fixed Function 的约束边界:API 不提供编程接口,因为硬件本身不支持程序执行。Fixed Function 阶段确立了 GPU 图形加速的基本技术框架,也定义了"CPU 提交命令、GPU 执行渲染"这一贯穿后续所有架构的基本分工模式。
1.2 Programmable Shader
Fixed Function 的灵活性瓶颈触发了第一次重大的架构重分配。二十一世纪初,GPU 开始引入可编程处理单元,用软件控制替代部分硬编码逻辑。NVIDIA GeForce 3 (NV20) 与 ATI Radeon 9700 (R300) 等产品率先落地 Programmable Shader,允许开发者以编程方式控制渲染管线的特定阶段。
硬件路径特征
Programmable Shader 将渲染管线中的部分阶段从硬编码电路替换为可编程执行单元。最早实现的是 Vertex Shader (VS) 和 Pixel Shader (PS)(在 OpenGL 中 PS 称为 Fragment Shader, FS)。VS 负责顶点级别的几何变换与光照计算,替代了 Fixed Function 的 Transform and Lighting (T&L) 单元。PS/FS 负责像素级别的颜色计算与纹理采样逻辑,替代了 Fixed Function 的纹理映射与颜色混合的一部分。
这一阶段硬件路径发生了关键变化:渲染管线中出现了"通用计算单元执行开发者自定义代码"的环节。可编程单元仍局限于特定阶段,但其内部指令序列由软件决定,硬件不再限定具体的计算方法。
Data Movement 特征
Programmable Shader 引入了可编程的 data movement 能力,但被严格限定在各自阶段内部。VS 可以读取顶点属性数据,执行自定义计算后输出变换后的顶点位置与 varyings。PS 可以读取纹理数据(仍走 Texture / Sampler Path)和插值后的顶点属性,输出自定义的像素颜色。阶段之间的 data movement 仍由固定管线控制:VS 的输出必须按固定格式传递给光栅化器,PS 的输入必须由光栅化器按固定规则生成。开发者获得了阶段内数据读写的控制权,但阶段间的数据流转仍由硬件固定。
Dependency Tracking 特征
Programmable Shader 引入了编译器可知延迟依赖与运行时延迟依赖的初步区分。ALU-ALU 等短延迟操作由编译器通过指令调度处理,纹理采样等可变延迟操作则需要运行时动态等待。早期 GPU 通过简单的流水线 interlock 或 stall 机制处理这些依赖,但具体实现已开始因厂商而异。这一阶段的 dependency tracking 仍大部分隐藏在硬件内部,开发者通过高级着色语言编写代码,由驱动编译为硬件指令,依赖管理细节对开发者不透明。
开发者控制权
开发者首次获得了在 GPU 上执行自定义代码的能力。控制权转移有两层含义。开发者可以定义顶点变换与像素着色的具体算法,突破 Fixed Function 的视觉风格限制。代价是要承担编程责任:学习着色语言(HLSL、GLSL、Cg),理解 GPU 并行执行模型,管理寄存器与指令级效率。
VS 让开发者能实现自定义的顶点动画、形变和几何细节生成。PS/FS 让开发者能实现自定义光照模型、材质效果和后处理技术(如景深、辉光)。这些能力直接推动了实时图形视觉复杂度的提升。
新增的失败模式
Programmable Shader 引入了多重新的失败模式:
性能不可预测性。软件编程的灵活性意味着性能不再由硬件规格简单决定。相同的视觉效果可以通过不同的着色器实现,性能差异可能达数量级。开发者需要理解指令级效率、寄存器压力、纹理采样延迟等因素。
编程模型开销。高级着色语言到硬件指令的编译引入了抽象层开销。驱动中的着色器编译、优化、寄存器分配过程增加了启动延迟和运行时不确定性。
架构异构性。早期可编程管线保留了大量 Fixed Function 单元(光栅化器、深度测试、混合单元等),整体架构呈"可编程岛 + 固定功能海"的异构格局。开发者需要同时理解可编程部分与固定部分的交互约束,优化难度增加。
分支与发散。可编程代码引入了控制流,而 GPU 的 SIMD 执行模型对分支发散敏感。早期的 Programmable Shader 对动态分支的支持有限,不当的控制流可能导致严重的性能退化。
对 Graphics API 的影响
Programmable Shader 促成了 Graphics API 的架构升级。DirectX 8 / 9 和 OpenGL 2.0 围绕可编程管线重新设计了接口,引入了高级着色语言支持。API 的角色从"硬件状态配置器"向"workload 描述接口"演进:开发者编写着色器程序定义渲染任务的计算逻辑,API 负责将程序提交给 GPU 执行。这一转变为后续 GPGPU 的兴起铺设了前提,可编程管线的存在证明了 GPU 执行非固定图形计算的可行性。
1.3 Unified Shader Architecture
Programmable Shader 将部分渲染阶段交给可编程单元执行,但 VS 和 PS 仍由物理上独立且功能专一的硬件单元处理。分离式设计在负载不均衡场景下暴露出资源利用效率问题:当顶点处理成为瓶颈时,Pixel Shader 单元可能空闲。当像素处理成为瓶颈时,Vertex Shader 单元可能闲置。硬件资源的静态划分限制了整体吞吐效率。
硬件路径特征
Unified Shader Architecture (USA) 在 2006 至 2007 年间被引入(NVIDIA GeForce 8 系列 / G80,AMD Radeon HD 2000 系列 / R600)。USA 取消了 VS 单元和 PS 单元在物理层面的严格区分,代之以一组统一的、可编程的 Stream Processor (SP)。不同厂商对 SP 有不同命名(NVIDIA 称为 CUDA Core,AMD 称为 Shader Core),但设计思想一致:同一组通用执行单元能够动态执行 Vertex Shading、Pixel Shading、Geometry Shading 乃至通用计算指令。
USA 的关键硬件创新在于前端调度器与统一执行后端之间的解耦。Hardware Scheduler 根据实时负载需求,将不同类型的着色任务动态分配给空闲的 SP 单元,消除了 Fixed Function 和早期 Programmable Shader 架构中的资源静态绑定。
Data Movement 特征
USA 改变了 data movement 的组织方式。分离式架构中,顶点数据与像素数据走独立的输入输出路径。统一架构中,所有类型的数据都通过统一的 load/store 路径进入执行单元。由此产生了新的 data movement 特征:
统一加载路径。所有 shader stage 共享相同的全局内存访问路径(Global LD/ST Path),不再区分"顶点数据端口"和"像素数据端口"。
片上存储复用。同一组 Local Shared Storage 和 Vector RF 可以被不同类型的 workload 动态占用,存储资源的分配由运行时决定,而非硬件固定。
数据局部性挑战。不同 shader stage 对数据局部性的访问模式不同:顶点处理偏向规则的数据流(vertex buffer 顺序访问),像素处理偏向二维空间局部性(texture 访问、framebuffer 邻域访问)。统一执行单元需要同时适应这两种访问模式,对 cache 设计和 data prefetch 策略提出了更高要求。
Dependency Tracking 特征
USA 的 dependency tracking 复杂性大幅上升。分离式架构中,VS 和 PS 之间的依赖关系由固定管线自然保证(VS 完成后才能开始 PS)。统一架构中,不同类型的任务可能并发执行于同一硬件单元,需要显式的依赖管理。
Hardware Scheduler 承担了更多的 dependency tracking 责任:它必须追踪不同任务之间的资源依赖、数据依赖和顺序依赖,决定哪些任务可以并行、哪些必须串行。这一阶段的 dependency tracking 开始呈现厂商差异,有的方案倾向 hardware-managed scoreboard 风格,有的方案倾向 compiler-inserted wait 指令配合 hardware counter 的风格。统一原则是 dependency tracking 从"固定管线自然保证"转向"显式硬件/软件协同管理"。
开发者控制权
开发者获得了跨 shader stage 的编程统一性。同一套编程模型、同一组指令集可以编写 VS、PS、GS 乃至通用计算 kernel,降低了多 stage 开发的认知成本。不过开发者对底层资源调度的控制权仍然有限:Hardware Scheduler 的任务分配策略、SP 单元的具体绑定关系对开发者不透明。开发者可以通过 shader 代码影响 workload 特征,但无法直接控制任务到硬件的映射。
新增的失败模式
调度复杂性。统一架构引入了 Hardware Scheduler 这一关键组件,其调度策略直接影响资源利用效率。调度不当可能导致 SP 单元负载不均衡、cache thrashing、或任务切换开销过高。这些失败模式在分离式架构中不存在。
资源竞争。多个 shader stage 共享同一组 SP、RF 和 Local Shared Storage,资源竞争成为新的瓶颈来源。一个过度占用寄存器的 pixel shader 可能降低整个 GPU 的 occupancy,影响 vertex shader 的并行度。
峰值性能 trade-off。统一架构的通用性通常意味着特定任务的峰值性能略低于高度优化的专用单元。当 workload 高度偏向某一类任务(纯顶点处理或纯像素处理)时,USA 可能无法达到分离式专用架构的理论峰值。
性能分析困难。统一执行使性能瓶颈定位更加复杂。分离式架构中,VS 慢就是 VS 单元瓶颈、PS 慢就是 PS 单元瓶颈。统一架构中,同一个 workload 可能在 SP 单元、scheduler、memory path 之间形成复杂的瓶颈交互。
对 GPGPU 的意义
USA 是 GPGPU 的硬件前提。统一执行单元意味着 GPU 不再区分"图形计算"和"通用计算",同一组 SP 既可以执行 shader,也可以执行通用并行 kernel。这一设计消解了图形管线与通用计算之间的硬件壁垒,GPU 由此具备了向通用并行处理器角色演进的条件。
1.4 General-Purpose Computing on GPU (GPGPU)
Unified Shader Architecture 证明了同一硬件可以执行多种类型的并行计算任务。学术界和工业界开始探索:GPU 的并行执行模式能否用于非图形领域?General-Purpose computing on GPU (GPGPU) 由此成为一条独立的架构演进路线。
硬件路径特征
GPGPU 的要点在于重新定义了既有硬件的使用方式,没有引入新的硬件单元。GPU 的 SP 阵列原本为图形渲染设计,其执行模型(大量线程并行执行相同指令,通过 SIMD datapath 处理数据)与许多科学计算和数据分析任务的特征吻合。GPGPU 的核心硬件路径变化在于:原有 graphics pipeline 的前后端固定单元被绕过或重组,计算 workload 直接从 command processor 进入统一执行阵列,输出直接写入 global memory,不经过光栅化器和 Pixel Backend。
这条路径使 GPU 内部形成了独立的 compute path:command processor 解析 dispatch 命令 → scheduler 分配 workgroup → SP 执行 kernel → 结果写回 Device Memory。compute path 与 graphics path 共享执行资源和 memory system,但避免了 graphics fixed-function 单元的约束。
Data Movement 特征
GPGPU 引入了新的 data movement 模式,也带来了新的 data movement 挑战:
Host-Device 数据传输。独立 GPU (dGPU) 通过 PCIe 总线与 CPU 连接。CPU 控制的 Host Memory 与 GPU 的 Device Memory 物理分离,数据在两者之间传输必须经过 PCIe 链路。PCIe 带宽远低于 Device Memory 本地带宽,且延迟较高。即使 GPU 计算速度极快,数据搬运也可能成为整体性能的制约因素。
Device Memory 层次结构。GPGPU workload 需要显式管理 Device Memory 内部的数据放置。Global Memory(高带宽、高延迟)、Local Shared Storage(低延迟、片上、workgroup 内共享)、Vector RF(每个 invocation 私有、最低延迟)形成了明确的层次结构。开发者需要理解各层次的带宽、延迟和容量约束,决定数据在层次之间的流动策略。
跨 workgroup 数据交换。与图形渲染中数据流主要沿管线单向流动不同,通用计算中 workgroup 之间可能需要通过 global memory 进行数据交换。这种跨 workgroup 通信引入了 synchronization 和 memory ordering 需求,data movement 不再是单向的。
Dependency Tracking 特征
GPGPU 的 dependency tracking 从 graphics workload 的隐式依赖模式转向显式的 software-managed 依赖模式。Graphics pipeline 中,stage 之间的依赖由硬件固定(VS 必须在 PS 之前)。GPGPU workload 中,kernel 之间的依赖、workgroup 内部的同步、workgroup 之间的顺序都需要显式管理。
Memory dependency 成为核心问题。Kernel 中的 load/store 操作具有可变延迟,依赖跟踪需要区分:哪些依赖可以在编译时确定延迟(ALU-ALU 依赖),哪些依赖只能在运行时确定(global memory load 依赖)。不同厂商采用不同策略,有的使用 hardware-managed scoreboard 在运行时判断 logical warp 的就绪状态,有的通过 compiler 插入 wait 指令配合 hardware dependency counter,有的将大部分依赖管理隐藏在硬件内部。这些策略在依赖跟踪的硬件开销、编译器复杂度、软件可控性之间做出了不同 trade-off。
Barrier synchronization 是 GPGPU 引入的新依赖类型。Workgroup 内部的 barrier 要求所有 thread 到达同步点后才能继续执行,跨 workgroup 的同步则需要通过 global memory 和 atomic 操作实现。这些同步机制增加了 dependency tracking 的复杂性。
开发者控制权
GPGPU 将大量控制权交给开发者,也带来了相应的责任。开发者需要显式管理:
Work 划分。将计算任务分解为适合 GPU 并行执行的 workgroup 和 invocation 网格。划分策略直接影响 occupancy 和 latency hiding 能力。
Memory 层次使用。决定哪些数据放在 global memory、哪些放在 Local Shared Storage、哪些保留在 register 中。不当的 memory 使用策略可能导致 bandwidth bottleneck 或 occupancy 下降。
Synchronization。显式插入 barrier、memory fence 和 atomic 操作来保证正确性。Synchronization 不足导致 data race。过度同步则降低并行效率。
新增的失败模式
数据传输瓶颈。Host-Device 数据传输带宽远低于 GPU 计算吞吐。如果 workload 需要频繁的数据交换,PCIe 带宽可能成为瓶颈。
Occupancy 不足。Register 用量过多、Local Shared Storage 用量过大、或 barrier 使用不当可能导致 resident logical warp 数量不足,削弱 GPU 通过 thread-level parallelism 隐藏延迟的能力。
Memory divergence。Warp/wavefront 内的不同 lane 访问相距较远的 global memory 地址时,coalescing 效率下降,memory transaction 数量增加,有效带宽降低。
Branch divergence。Kernel 中的条件分支导致 warp 内部分 lane 不活跃,SIMD 利用率下降。
早期 GPGPU 的映射开销。在 CUDA/OpenCL 出现之前,开发者需要将通用算法伪装成图形渲染操作(将数据编码为纹理、将计算编码为 shader),开发效率极低且限制了算法设计空间。
编程模型的发展
2007 年 NVIDIA 推出 CUDA,提供了针对 GPU 的类 C 编程框架。Khronos Group 随后发布 OpenCL 开放标准。这些编程模型让开发者可以直接用通用计算语言编写 GPU kernel,无需通过图形 API 间接访问。CUDA 和 OpenCL 的流行推动 GPU 在 HPC、机器学习、计算机视觉等领域得到广泛应用。
2010 年代中后期,深度神经网络(DNN)尤其是 CNN 和 Transformer 的兴起,使大规模并行矩阵运算需求激增。GPU 的 SPMD / SIMT-like 执行模型与矩阵运算的数据并行特征匹配,加速了 GPGPU 架构演进。GPU 设计开始同时考虑 graphics workload 和 compute workload 的双重需求,架构决策需要在两种 workload 的 data movement 模式和 execution pattern 之间寻找平衡。
1.5 Specialized Acceleration Units
GPGPU 进一步扩展了通用计算能力,但通用可编程单元在执行某些固定模式的高频计算时并非最优。对于计算模式固定、吞吐量要求极高的特定任务,专用硬件单元能以更低的功耗和面积成本达到更高的执行效率。GPU 架构由此发生又一次重分配:fixed-function ownership 从通用执行单元部分回归到专用电路。
专用单元的重新引入不代表回到 Fixed Function 时代。它与 Unified Shader 和 GPGPU 的通用计算能力共存,构成"通用为基础、专用为补充"的混合架构。工程动机是在功耗预算和芯片面积约束下,为最高频、最固定的计算模式提供硬化路径,同时保留通用单元的灵活性处理其余 workload。
硬件路径特征
专用加速单元将特定计算路径从通用 SP 中拆分出来,形成独立的 execution pipe。每个专用单元负责一类固定模式计算:
Tessellation Unit。负责根据输入的 Patch 和控制参数动态生成细分后的几何网格。将几何细分这一固定模式计算从通用 shader datapath 中移出,由专用电路执行顶点的参数化生成和插值。
Ray Traversal Unit。负责加速 BVH(Bounding Volume Hierarchy)遍历和射线-三角形求交计算。将光线追踪中重复执行的 traversal / box test / triangle test 路径硬化,降低 intersection 测试的 ALU 成本。
Matrix / Tensor Acceleration Unit。负责加速矩阵乘加(Matrix Multiply-Accumulate, MMA)运算。将深度学习和高性能计算中大量出现的矩阵运算从通用 ALU/FPU 路径中移出,通过硬化的 tile-shaped datapath 和 accumulator 实现更高的 arithmetic throughput。不同厂商对此类单元的命名不同(NVIDIA Tensor Core、AMD Matrix Core),但设计目标一致。
Multi-View Rendering 与 VRS 单元。针对 VR 等需要为多个视点生成图像的场景,以及屏幕不同区域需要不同着色密度的场景,提供硬件级的 workload reduction 路径。
Data Movement 特征
专用单元的引入改变了 data movement 的组织方式。每个专用单元都有自己的 operand movement path:
Tessellation Unit 的数据路径。输入 Patch 数据(控制点位置、细分因子)从 Device Memory 经 Vertex Fetch Path 进入 Tessellation Unit,生成的细分顶点数据输出到统一执行阵列的 Domain Shader 阶段。这一过程减少了原始顶点数据的传输量:只需传输低多边形的 Patch 数据,细分后的高密度网格在片上生成。
Ray Traversal 的数据路径。BVH 加速结构和射线数据从 Device Memory 经专用 RT Traversal Memory Path 读取,traversal 状态(当前节点、射线参数)在专用单元内部维护,hit/miss 结果返回给 shader 进行 shading 计算。Traversal 过程中的内存访问模式(随机访问 BVH 节点)与 graphics workload 的顺序访问模式不同,专用路径可以针对这种访问模式优化 cache 和 prefetch 策略。
Matrix / Tensor 的数据路径。矩阵操作数从 Vector RF 或 Local Shared Storage 经专用 Matrix Operand Movement Path 进入 acceleration unit,计算结果写回 accumulator register。这一路径与通用 ALU 的 register-to-register 操作路径不同:矩阵运算需要同时读取大量操作数,operand movement 的带宽需求和组织方式都有专门设计。
关键约束:专用单元的 data movement 路径通常与通用执行单元共享最终的 L2 cache 和 memory controller 接口。当专用单元和通用单元同时发起高带宽内存请求时,会产生 fabric arbitration 竞争,可能导致延迟上升和 throughput 抖动。
Dependency Tracking 特征
专用单元的 dependency tracking 呈现"混合模式":专用路径内部的操作依赖由硬件自动管理(类似 Fixed Function 的流水线 stall/bubble),但专用单元与通用 shader 之间的交互需要显式同步。
专用单元内部。Tessellation Unit 内部的细分流程、RTU 内部的 traversal 步骤、Tensor Unit 内部的矩阵运算流水线,其依赖关系由硬件硬编码管理,开发者不直接控制。
专用单元与通用单元的边界。当 shader 调用专用单元(如 shader 发起 ray query 或 matrix 运算)时,需要处理跨单元的依赖跟踪:通用 shader 的输出作为专用单元的输入,专用单元的输出作为通用 shader 的输入。这一 crossing 点的 latency 和 synchronization 机制成为性能优化的关键点。不当的调用粒度或数据依赖模式可能导致 shader core under-utilization(等待专用单元返回)或专用 unit stall(等待 shader 提供输入)。
开发者控制权
开发者对专用单元的控制权表现为"调用接口"而非"内部编程"。开发者决定何时调用专用单元、传递什么参数、如何处理输出,但无法更改专用单元内部的执行逻辑。这种控制权模型的边界需要仔细理解:
Tessellation。开发者通过 Hull Shader 和 Domain Shader 控制细分因子的计算和细分后顶点的处理,但细分算法本身(如何根据因子生成网格拓扑)由硬件固定。
Ray Tracing。开发者通过 Ray Generation Shader、Closest Hit Shader、Miss Shader 等控制射线行为和命中后的 shading 逻辑,但 BVH traversal 和 intersection test 由硬件固定。
Matrix 运算。在编程模型层面(如 CUDA wmma、HIP mfma),开发者指定矩阵维度、数据布局和运算类型,但具体的 tile shape、accumulator 组织和指令调度由硬件和编译器决定。
新增的失败模式
利用率不足。专用单元只能高效执行预设任务。如果 workload 特征与专用单元不匹配(如射线分布高度不规则导致 BVH traversal 效率低、矩阵维度不符合硬化 tile shape),专用单元可能处于低效或闲置状态,造成芯片面积和功耗预算的浪费。
跨单元同步开销。专用单元与通用 shader 之间的数据传递和同步引入了额外 latency。Ray tracing workload 中,shader 可能频繁调用 RTU 并等待返回,导致 logical warp eligibility 下降和 shader core utilization 不足。
数据准备开销。专用单元的高执行效率依赖于输入数据的正确布局。矩阵运算需要操作数按特定 tile shape 排列,光线追踪需要预构建的 BVH 加速结构。数据准备和格式转换的成本可能抵消专用单元的执行优势。
Pipeline coupling。专用单元的引入增加了 GPU 内部 pipeline 的复杂性。一个专用单元的 stall 可能通过 backpressure 影响上游的通用执行单元,或者通过 resource contention 影响并行的其他 workload。
编程模型约束。专用单元通常通过特定 API 或语言扩展暴露(如 Vulkan Ray Tracing 扩展、CUDA warp-level matrix 操作)。这些接口的语义和约束限制了算法设计的灵活性,开发者需要在专用加速和通用性之间做出取舍。
1.6 反复再平衡:从 Fixed Function 到混合架构
回顾上述五个阶段,GPU 架构演进的核心逻辑可以归纳为四项要素之间的反复重分配:
| 阶段 | Fixed-Function Ownership | Programmability | Data Movement 控制 | Dependency Tracking 控制 |
|---|---|---|---|---|
| Fixed Function | 硬件完全拥有 | 开发者几乎无控制权 | 硬件固定路径 | 硬件完全管理 |
| Programmable Shader | 部分转移到软件 | VS/PS 可编程,阶段间固定 | 阶段内可编程,阶段间固定 | 部分暴露给编译器 |
| Unified Shader | 执行单元统一化 | 全 stage 统一编程模型 | 统一 load/store 路径 | Hardware Scheduler 管理 |
| GPGPU | 绕过 graphics fixed-function | 通用计算编程模型 | 显式 memory 层次管理 | 显式 software-managed |
| Specialized Units | 选择性回归专用硬件 | 调用接口可编程,内部固定 | 专用 operand movement 路径 | 混合模式 |
这一演进的实质是反复再平衡,既不是单向的"从固定到通用",也不是"从通用到专用"。每一次重分配都解决了前一阶段的瓶颈,同时引入了新的约束和失败模式。
再平衡的内在逻辑
Fixed Function 追求执行效率但缺乏灵活性。Programmable Shader 将灵活性还给开发者但引入异构性和编程开销。Unified Shader 消除资源静态划分但增加调度复杂性。GPGPU 将通用计算推向极致但 Host-Device 数据传输和显式同步成为瓶颈。Specialized Units 为最高频计算模式重新引入固定功能但增加了跨单元同步和数据准备成本。
这个循环会持续下去,因为四项要素(fixed-function ownership、programmability、data movement control、dependency tracking control)之间不存在全局最优的分配方案。最优分配取决于 workload 特征、工艺约束、功耗预算和应用需求的动态变化。理解这一点,比记住"GPU 性能持续提高"更有分析价值。
与 Graphics API 的衔接
GPU 架构的演进直接塑造了 Graphics API 的设计哲学。Fixed Function 时代,API 是状态机,开发者设置状态参数,硬件按固定逻辑执行。Programmable Shader 时代,API 演变为 workload 描述接口,开发者编写着色器程序定义计算逻辑。Unified Shader 和 GPGPU 时代,API 进一步演进为资源管理和同步控制接口,开发者既要定义计算逻辑,又要显式管理内存、barrier 和 synchronization。
理解 GPU 架构史上控制权如何在硬件与软件之间反复重分配,是理解 Graphics API 设计选择的必要前置。每一个 API 机制的背后,都对应着某一阶段架构演进所引入的 data movement 模式、dependency tracking 需求或 fixed-function / programmability 边界。
二、Graphics API
上一章从架构演进角度梳理了 GPU 硬件路径的变迁:控制权在 fixed-function 与 programmability 之间反复重分配,data movement 模式随之变化,dependency tracking 的复杂度逐步上升。Graphics API 正是这些硬件变迁在软件层面的映射,API 的设计哲学直接由 GPU 架构的阶段性特征决定。
Graphics API 定义了应用程序与 GPU 硬件之间的软件契约。它规定了一组标准化的函数调用、对象模型、状态转换规则与同步语义,开发者依此描述渲染与计算任务的意图。现代 Graphics API(Vulkan、DirectX 12、Metal)向开发者暴露显式控制能力:Explicit Memory Management、Explicit Barrier、Explicit Synchronization、Multi-threaded Command Recording 与 Pipeline State Object(PSO)。这些能力的引入目标在于降低 CPU 执行开销,同时为 GPU 并行执行提供更大的调度自由度。
理解 Graphics API 的关键不在于记住每个函数签名,而在于识别 API 契约与硬件现实之间的多层翻译。API 声明的 stage 不等于硬件单元,API 定义的 barrier 不等于单一 cache flush,API 描述的 pipeline 不等于硬件数据流。后续各章对 GPU 架构与微架构的分析,需要建立在对这一分层契约的清醒认知之上。
2.1 API Abstraction 与 Hardware Reality 的边界
在展开各概念之前,先明确分层契约:
Application
→ Graphics / Compute API
→ Runtime / Driver
→ Shader Compiler
→ IR / Native ISA
→ Command Stream / Descriptor / Pipeline State
→ Command Processor / Front-End
→ Macro-Core / Fixed-Function Backend
→ Memory System
每一层都对上层提供的抽象进行翻译、重构或硬件映射。以下断言需要前置明确:
API 不是硬件。 API 声明的 IA→VS→HS/Tess/DS→GS→RS→PS→OM 是逻辑处理流程,不是硬件中数据必须依次经过的物理路径。硬件实现可以合并、重排或并行执行其中多个阶段。
Shader source 不是 native machine code。 HLSL/GLSL/MSL 是面向开发者的高级表达。它们需要经过 compiler 翻译为中间表示(SPIR-V、DXIL、AIR),再经 driver 后端编译为特定 GPU 的 native ISA。
SPIR-V / DXIL / AIR / PTX 不是所有 GPU 直接执行的代码。 这些 IR 是 compiler 与 driver 之间的契约层,不是硬件原生指令格式。GPU 硬件执行的是特定厂商的 native ISA(如 NVIDIA SASS、AMD GCN/RDNA ISA),其格式通常不公开。
Driver 不是 API 转发器。 Driver 承担 shader 编译(register allocation、instruction scheduling、scalarization)、command buffer 构建、resource state tracking、residency management、barrier lowering 与 firmware 交互。Command buffer 未必是硬件可直接识别的最终指令码,driver 可能进一步翻译或插入微码。
Compiler 的深度参与。 从 shader source 到 native ISA 的过程中,compiler 决定 register allocation、instruction scheduling、scalarization、vectorization、memory dependency placement、spill decision 与 descriptor access lowering,直接影响 occupancy、memory traffic 与指令级并行度。
Hardware 看到的是 command stream、pipeline state、descriptor、native instruction、memory address、resource state 与 synchronization primitive 的硬件映射。 API 层定义的抽象概念在这一层被转换为具体的寄存器值、内存地址与控制信号。
这一分层模型的工程含义是,分析 GPU 性能问题时,需要追踪一个操作从 API 声明到硬件执行的完整路径,判断瓶颈出现在哪一层。API 层的"合理"操作可能在 driver 层引入额外开销,或在 hardware 层走上一条高延迟路径。
2.2 Memory
GPU 的并行执行模型对内存子系统有刚性约束。数千个 invocation 同时发起数据访问,内存子系统必须在带宽、延迟与一致性之间维持平衡。理解 GPU 内存管理需要区分不同 state space 的路径差异:Texture Path、Constant / Uniform Path、Surface / UAV Path 与 Global LD/ST Path 在地址生成、请求合并、cache 层级与依赖解除机制上各不相同。
2.2.1 Device Memory
Device Memory 是物理上位于 GPU 板卡、通过专用高速总线连接到 GPU 芯片的内存。常见类型包括 GDDR 系列(GDDR6、GDDR6X、GDDR7)与 HBM 系列(HBM2、HBM2e、HBM3)。GDDR 在成本与带宽之间达到平衡,HBM 通过 DRAM 堆叠与宽接口提供更高带宽和更优的每比特功耗,多见于高端 GPU。
Device Memory 的设计优先追求高带宽(数百 GB/s 至数 TB/s),以满足大量并行执行单元同时访问数据的需求。其访问延迟远高于 CPU 缓存。GPU 通过 Thread-Level Parallelism(TLP)与快速上下文切换(Stall Swap)机制隐藏这一延迟。
GPU 内部的处理单元(Shader Core、Texture Unit、Pixel Backend)直接高速访问 Device Memory。性能敏感数据应存放于此:纹理、顶点/索引缓冲、渲染目标(Color/Depth/Stencil)、Compute Shader 所需的 UAV / Storage Buffer,以及光线追踪加速结构。
在传统图形 API(如 DirectX 11、OpenGL)中,Device Memory 的分配与管理通常由图形驱动程序隐式处理。在现代图形 API(如 DirectX 12、Vulkan、Metal)中,开发者拥有更显式的控制权,可以明确选择不同类型的内存堆(Heap),例如纯 GPU 本地堆(Device Local Heap),或同时对 CPU 可见的 GPU 本地堆。开发者需手动管理资源的分配、绑定与生命周期。分配内存时通常需满足特定的对齐(Alignment)要求,以确保最佳的访问性能。显式管理提供了更大的优化空间与灵活性,但也增加了编程复杂性。
在拥有 dGPU 的系统中,CPU 访问 Device Memory 必须通过 Peripheral Component Interconnect Express(PCIe)总线进行。PCIe 总线的带宽远低于 GPU 访问其本地内存的带宽,且延迟相对较高。CPU 直接读写 Device Memory 效率极低,通常应避免。CPU 与 GPU 间的数据传输通常依赖于临时缓冲区(Staging Memory)和 DMA Engine 来实现。理解并合理规避 PCIe 带宽瓶颈是设计 CPU-GPU 数据传输策略的前提。
2.2.2 Host Memory
Host Memory 是系统主内存(RAM),由 CPU 与操作系统管理。GPU 可通过 PCIe 访问 Host Memory,但速度远低于 Device Memory。
Pageable Memory
Pageable Memory 的物理地址不固定,操作系统可能随时将其换入换出或在物理 RAM 内重排。GPU 的 DMA Engine 通常要求固定物理地址,因此无法直接对 Pageable Memory 执行 DMA。Driver 在后台自动将数据从 Pageable Memory 拷贝到临时 Staging Buffer(Pinned Memory),再启动 DMA。该过程对应用透明,但带来额外 CPU 开销与延迟。部分系统支持 IOMMU,允许 GPU 使用虚拟地址访问 Pageable Memory,由硬件负责地址转换。GPU 直接访问 Pageable Memory 效率低下,应避免用于高性能场景。
Pinned Memory / Staging Memory
Pinned Memory 的物理地址由操作系统保证固定,直至释放前不会被移动或换出。GPU DMA Engine 可直接对其读写,是 CPU-GPU 异步数据传输的基础机制。
传输流程:
- CPU 在 Pageable Memory 中准备数据。
- CPU 分配 Pinned Memory 作为 Staging Buffer。
- CPU 将数据从 Pageable Memory 拷贝到 Pinned Memory。
- CPU 通过驱动命令 GPU 的 DMA Engine,从 Pinned Memory 异步传输数据至 Device Memory。
- CPU 继续执行其他任务,无需等待 DMA 完成。
- 使用 Fence/Event 确认 DMA 完成,GPU 可安全使用数据。
- 释放 Pinned Memory(GPU → CPU 方向相反)。
Pinned Memory 分配开销较大,占用物理内存且不可换出。过度使用可能影响系统稳定性,应合理分配并及时释放。
2.2.3 Unified Memory
Unified Memory 的目标是为 CPU 和 GPU 提供统一地址空间,简化数据共享。底层实现因硬件平台而不同,需从三个维度理解:
Physical UMA / Shared Physical Memory
UMA(Uniform Memory Access)架构主要应用于集成 GPU(iGPU)或 SoC 场景。CPU 与 GPU 共享同一物理内存(系统主存),无需通过 PCIe 显式传输。例如 Apple Silicon M 系列与部分 AMD APU 采用此类架构。尽管共享物理内存,CPU 与 GPU 的访问带宽和延迟并不完全相同。两者拥有独立的缓存层次结构,内存控制器负责仲裁访问请求。
API-visible Coherency Boundary
根据缓存一致性实现机制,UMA 可细分为 Hardware-Coherent UMA(hcUMA)。先进 SoC 设计实现硬件级缓存一致性(如 MESI 或 MOESI 协议变种),硬件自动维护 CPU 缓存与 GPU 缓存中共享数据的一致性。当一方修改数据时,硬件自动传播修改或使其他缓存副本失效。
这种 coherency guarantee 通常只在 command buffer boundary 上成立,而非"任意时刻自动细粒度一致"。硬件一致性协议引入额外内部互连流量与延迟开销。
Programming Model
不同 API 提供不同的统一内存编程模型:
- CUDA Unified Memory:提供 Managed Memory 模型,CPU 与 GPU 共享统一虚拟地址空间。数据在物理上仍可能分布于 Host Memory 与 Device Memory,操作系统与 driver 通过追踪访问模式在后台自动执行数据页迁移(On-Demand Paging/Migration)。开发者可通过 API 提供 hints 指导迁移策略。
- DirectX 12:
ID3D12Resource::Map()返回 CPU 侧虚拟地址(CPU VA),GetGPUVirtualAddress()返回 GPU 侧虚拟地址(GPU VA)。两者是独立地址,并非同一指针。开发者需显式管理数据同步与传输。 - Vulkan:
vkGetBufferDeviceAddress()暴露 GPU 侧虚拟地址(Buffer Device Address, BDA),允许 GPU 在 shader 中直接访问缓冲区地址。Vulkan 不提供 CPU/GPU 共用地址空间,需显式管理传输与同步。
这些编程模型在简化数据共享的同时引入了各自的复杂性。Managed Memory 的 on-demand paging 在 page fault 时引入较大延迟,不适合对延迟敏感的热路径。显式虚拟地址模型需要开发者手动管理数据传输,但性能更可预测。开发者应根据 workload 特征、数据访问模式与延迟要求选择合适的编程模型,并合理利用 API 提供的 hints 与 prefetch 机制优化数据迁移。
On-Demand Paging 的硬件流程
按需分页解决的核心问题是:GPU kernel 在执行过程中访问尚未驻留在本地显存的页面时,如何在不终止线程的前提下完成透明迁移。以离散 GPU 为例,完整流程如下:
- GPU 线程访问一个未映射的虚拟地址,L1/L2 TLB 均 miss。
- GPU MMU 执行 page walk,本地页表中无对应 PTE,触发 page fault。
- 硬件挂起(suspend)当前 warp,保留其寄存器与执行状态,而非直接终止。
- fault 进入 GPU 侧的 page fault queue,经 PCIe 或 NVLink 中断通知 CPU runtime。
- CPU runtime 对队列中的请求排序并合并,同时预取相邻页以分摊延迟。
- 执行迁移:先在源端(CPU 侧)解除映射(unmap),再分配 GPU 物理页并写入新 PTE(map)。若 GPU 物理内存已满,需先按 LRU 策略 evict 旧页回 CPU,再 fetch 新页,两步严格串行,先释放再分配,否则 PTE 状态不一致。
- 刷新 GPU 侧 uTLB(invalidate 对应条目),恢复 warp 调度,重新执行 faulting instruction。
关键约束在于:GPU 不具备 precise exception 处理能力,整个 fault 处理逻辑由 CPU runtime 驱动,GPU 侧仅负责挂起与重试。单次 page fault 处理延迟约数十微秒,瓶颈落在 OS 调度延迟(中断响应与线程唤醒),page walk 本身反而不是主因。这解释了为何 on-demand paging 不适合热路径:每次 fault 都意味着一次跨总线往返加一次 OS 上下文切换。
较新的架构在此基础上做了演进。GPU MMU 引入硬件 Page Walk Cache,本地命中时不再触发 CPU 侧 fault。高带宽互连(如 NVLink-C2C)支持 cacheline 粒度的远程访问,无需以整页为单位迁移。系统页表(4 KB/64 KB,CPU 维护)与 GPU 独占大页(2 MB)并存,兼顾灵活性与 TLB 覆盖率。
2.2.4 Cache Coherency & Memory Consistency
从 Coherency、Consistency/Ordering 与 Synchronization Primitive 三个层面分析。
Cache Coherency
Coherency 的核心问题:多个处理器(CPU 核心与 GPU 计算单元)均拥有同一内存地址的本地缓存副本时,一个处理器的修改如何被其他处理器正确观察?
典型 dGPU 系统中,CPU 缓存与 GPU 缓存之间通常不存在硬件级自动一致性维护机制。硬件不会主动确保 CPU 与 GPU 缓存的数据副本始终保持同步。若 CPU 修改了 Host Memory 中的数据而未采取额外措施,GPU 读取时可能得到过期(Stale)的数据副本。反之,若 GPU 修改了其缓存的数据,CPU 直接读取也可能得到过时的数据。
软件层面的解决方案:
- 显式缓存管理操作(Explicit Cache Management):部分 API 提供底层缓存控制指令,如 Flush(强制将缓存中修改的数据写回下一级内存)和 Invalidate(强制丢弃缓存中的数据副本)。这些操作通常由 driver 在执行内存屏障时隐式调用,开发者一般不需要直接操作,但需要理解其性能开销。
- 特殊内存类型(Special Memory Types):分配内存时可指定绕过缓存(Uncached)或使用特殊写入策略(Write-Combining)。Uncached 内存确保读写直接命中主存,保持一致性,但性能极低。Write-Combining 优化写入性能,主要用于 CPU 单向向 GPU 传输数据的场景,如纹理或顶点数据的上传。
Memory Consistency & Ordering
Memory Consistency Model 定义多处理器系统中内存操作的全局顺序规则。现代 GPU 通常采用宽松的内存一致性模型(Relaxed Consistency),允许大量内存操作重排序(RAW、WAR、WAW)。GPU 内部大量并行执行单元,数据写入全局内存需经过片上网络,写入可见性存在延迟(Visibility Latency)。GPU 以更弱的内存顺序保证换取更大的并行执行自由度。
Synchronization Primitives
在宽松内存模型下,开发者必须使用同步原语建立 happens-before 关系:
- Memory Barriers:通过 srcStageMask 与 dstStageMask 定义 Execution Dependency,通过 srcAccessMask 与 dstAccessMask 定义 memory dependency。Barrier 触发底层缓存管理操作,确保屏障前写入对屏障后读取可见。Vulkan 中 vkCmdPipelineBarrier 是核心 barrier 命令,允许精细控制 Execution Dependency 与 memory dependency 的范围。
- Atomic Operations:保证读取-修改-写入序列的不可中断性,用于计数器、锁等场景。Atomic 操作在不同 memory domain(device domain、host domain)之间的可见性需要额外的 memory barrier 保证。
- Fences 与 Events:Fence 用于 CPU-GPU 同步,Event 用于 GPU 内部细粒度同步,均建立 happens-before 关系。Vulkan 中 vkCmdSetEvent / vkCmdWaitEvents 提供 GPU 内部细粒度同步能力。
开发者必须理解这些同步原语的语义与开销,才能编写正确且高性能的 GPU 程序,避免难以调试的数据竞争(Data Race)与逻辑错误。过度同步导致性能下降,同步不足导致程序错误。
为什么 GPU 选择了与 CPU 不同的一致性方案
GPU 内存空间天然分为 thread-private(local memory、寄存器溢出)与 global 两类。Thread-private 数据仅对发出线程可见,可安全采用 write-back 策略。真正需要一致性维护的只有 global 地址空间。这一结构性差异是 GPU 一致性设计的出发点。
若将 CPU 式 MESI 协议移植到 GPU,学术模拟评估显示 L2 需要约 28% 的有效容量用于维护 directory 与 snoop filter 状态。更关键的问题在于 GPU 的写行为特征:大量数据属于 write-once(计算结果写出后不再修改),write-allocate 策略会为这些一次性写入分配缓存行并立即驱逐,引入无效写流量而无法获得后续命中收益。
学术界针对这一瓶颈探索了多种轻量化方案。Temporal Coherence(TC)为 L1 缓存行和 directory entry 附加 timestamp 与预期 lifetime,缓存行到达 lifetime 后自动 self-invalidate,无需 directory 逐次发起 invalidation 查询。TC-weak 变体相对 TC-strong 提升约 28% 性能并降低约 26% 片上互联流量(相关论文的 microbenchmark 数据),代价是牺牲部分即时可见性保证。GPU-VI(Valid/Invalid 两态协议)则走向另一个极端:L1 采用 write-through、no-write-allocate 策略,可写 global 数据不进入 L1,所有写操作直接下沉到 L2 统一仲裁。这是目前最简洁的硬件实现路径。
实际厂商的选择印证了上述权衡。NVIDIA 的 L1 对 global memory 默认采用 write-through / no-allocate 策略,等价于 GPU-VI,通过放弃 L1 写缓存换取零一致性维护开销。AMD RDNA 架构将 L1 私有化到每个 CU,采用弱一致性模型,跨 CU 可见性依赖显式 fence 指令。Apple 的统一内存架构利用 System Level Cache(SLC)作为全片一致性点,将一致性问题收敛到单一共享层级处理。
2.2.5 GPU Virtual Memory, Residency & Paging
GPU Virtual Memory(GPUVA)为 GPU shader 提供连续抽象的地址空间,简化编程并支持内存保护。不同应用或 GPU 任务可拥有独立虚拟地址空间,防止相互干扰。Over-subscription 允许申请比物理显存更大的虚拟地址空间。
Residency 与 Budget
GPU 资源在被访问前必须"驻留"到物理显存中。Residency Management 由操作系统或 driver 负责将虚拟内存页映射到物理显存。现代 API 提供 Budget 机制,允许应用查询可用显存量以规划资源驻留。
Page Fault 与 Page Migration
当 GPU 访问未驻留的虚拟地址时,发生 Page Fault。GPU 硬件暂停执行,控制权交给 driver。Driver 从系统内存加载所需页面到物理显存,更新地址映射,恢复 GPU 执行。Page Migration 是耗时操作,涉及内存传输与同步。
State Space Transaction Path 差异
不同 state space 的访问路径差异很大,理解这些差异是分析性能与 barrier 行为的基础:
Global LD/ST Path:
shader LD/ST instruction → lane address generation → active mask filtering
→ coalescing / request formation → L1 cache → TLB → L2 → fabric arbitration
→ memory controller → DRAM transaction → return queue → dependency clear
Texture / Sampler Path:
shader sample instruction → descriptor / resource header lookup
→ sampler state → coordinate transform / LOD → texture address generation
→ texture cache / block compression decode → TLB / page walk
→ L2 / memory → return to shader
Constant / Uniform Path:
shader constant reference → uniform buffer descriptor → broadcast path
→ constant cache → return to all lanes of execution group
Local Shared Storage Path:
shader LDS reference → per-workgroup address space → bank arbitration
→ SRAM read/write → return to requesting lanes
Pixel Backend Write Path:
fragment export → Pixel Backend (RB/ROP) → blend / depth / stencil
→ compression metadata (DCC/HTILE/TE/UTC) → L2 / memory
RT Traversal Memory Path:
ray dispatch → BVH node fetch → ray-box / ray-triangle test
→ traversal state → hit/miss shader scheduling → memory transaction
这些路径的差异决定了 barrier、layout transition、compression state 与 cache pollution 的代价不同。Texture path 涉及 descriptor cache、resource header fetch 与 TLB,延迟特征与 Global LD/ST path 明显不同。Constant path 走专用 broadcast 通道。Pixel Backend Write Path 经过 blend / depth / stencil 固定功能单元,天然与 compression metadata 耦合。Surface / UAV path 走 shader LD/ST,需显式处理 atomic 与 ordering constraint。
PS render target write 与 CS UAV write 走不同硬件路径。PS render target write 经过 Pixel Backend 的 blend / depth / stencil 单元与 compression metadata 处理。CS UAV write 走 shader LD/ST path,经 cache / atomic / ordering 处理。同样是写像素,路径不同导致 barrier cost、cache behavior 与 compression state 处理不同。
2.2.6 Memory Types and Heaps
现代显式 API 中,开发者需根据资源用途与硬件特性选择合适的 Memory Type 与 Memory Heap。
Memory Heaps
- Device Local Heap:物理位置为 Device Memory,GPU 直接访问速度最快。CPU 直接访问效率极低。适用于 GPU 频繁访问但 CPU 较少访问的资源。
- Host Visible Heap:位于 Host Memory,GPU 经 PCIe 访问,速度慢于 Device Local Heap。用于 CPU-GPU 共享数据交换场景,如 Staging Buffer。
- Unified Memory Heap:特定于 UMA 系统。CPU 与 GPU 共享物理内存,同时对两者可见,具备较低延迟。
Memory Types
Memory Type 的区分维度包括:
- CPU Visibility:内存是否可被 CPU 直接 Map 和访问。
- Cacheability:CPU 访问时是否经过缓存。Cached(需显式维护一致性)、Write Combined(批量合并写入)、Uncached(直接落物理内存)。
- GPU Locality:GPU 访问效率高低。
开发者的内存管理策略:根据资源用途与访问频率选择 Memory Type,使用 Staging Buffer 与 Pinned Memory 规避 PCIe 带宽瓶颈,通过 barrier、flush 与 invalidate 确保数据一致性。
Native ISA 的完整组成
Native ISA 不仅包含 opcode,还包含以下组成部分:predicate / active mask 控制、dependency hint / wait instruction、reuse flag、memory scope / cache operator、barrier / synchronization primitive、resource descriptor reference 与 addressing mode。这些组成部分共同决定硬件实际执行的行为,也是 compiler 与 hardware 之间的关键契约。只把 native ISA 理解为 opcode 集合会忽略 compiler 在依赖管理、资源分配和指令编排中的核心作用。
2.3 Pipeline
GPU 处理任务类型已从单一图形渲染扩展至 GPGPU、物理模拟、AI 推理与训练、实时光线追踪。任务扩展促成了执行路径的专门化,现代 GPU 采用多条针对特定任务优化的并行 Execution Pipeline。
2.3.1 Raster Pipeline
Raster Pipeline 将三维场景几何数据转换为二维像素图像。以下阶段顺序是逻辑流程:
Input Assembler(IA)
IA 从 Device Memory 的 Vertex Buffer 与 Index Buffer 中读取顶点属性数据,按 Vertex Layout 与 Primitive Topology 组装为 Primitive,传递到下一阶段。硬件实现通常包含专用 Vertex Fetch Unit,支持多种顶点压缩格式。
Vertex Shader(VS)
VS 对每个顶点独立执行坐标变换(Model Space → Clip Space)、每顶点光照、纹理坐标变换与 Skinning,输出变换后顶点位置与用于插值的 Varyings。
Hull Shader(HS)、Tessellator、Domain Shader(DS)
HS 计算 Tessellation Factors 并输出 Patch 常量数据;Tessellator 据此生成参数化顶点坐标;DS 接收二者输出,计算最终顶点位置与属性。
Geometry Shader(GS)
GS 接收完整 Primitive,可生成零个或多个新 Primitive。输出数量不确定且可能成为性能瓶颈,现代管线倾向于使用 Mesh Shader 或 Compute Shader 替代。
Rasterizer(RS)
RS 将三维 Primitive 投影到二维屏幕空间,确定覆盖像素,执行裁剪、屏幕映射、三角形设置与背面剔除。
Pixel Shader(PS)/ Fragment Shader(FS)
PS/FS 对每个 fragment 执行纹理采样、光照计算、阴影处理与材质属性计算,输出像素颜色值及可选的深度/模板值。由于每像素可能涉及多次纹理采样与复杂光照计算,PS 阶段常是工作负载瓶颈。
Output Merger(OM)
OM 将 Pixel Shader 输出与当前 Render Target 像素合并,执行深度测试、模板测试、混合(Blending)与 MSAA 处理。
逻辑流程不等于硬件数据流。 上述 IA→VS→HS/Tess/DS→GS→RS→PS→OM 是 API 层定义的逻辑处理顺序。硬件实现中,多个阶段可能合并执行,数据可能通过片上网络而非按阶段顺序传递。例如 Vertex Fetch 可能与 Shader Execution 深度耦合。Tessellation 数据可能不经过完整内存回写。Early-Z 测试可能在 PS 执行前完成部分深度剔除。
2.3.2 Compute Pipeline
Compute Pipeline 是通用并行计算管线,用于非图形的通用计算任务。
核心概念
- Compute Shader(CS):在 GPU 上执行的通用计算程序,可访问缓冲区、纹理与原子计数器。
- Workgroup:CS 的基本执行单元,由一组并行执行的 Invocation 组成,可通过 Local Shared Storage 共享数据并进行同步。
- Dispatch:CPU 通过 Dispatch 命令启动 Compute Shader,指定 Workgroup Grid 的维度。
执行模型
Dispatch 命令提交后,GPU 启动指定数量的 Workgroup。每个 Workgroup 内的 Invocation 并行执行 CS 代码,通过 Local Shared Storage 与 barrier 协作。不同 Workgroup 之间通常独立,可通过全局内存与 atomic 操作通信。
Compute Shader 不经过 Raster Pipeline 的固定功能阶段(Rasterizer、Pixel Backend),因此适用于不适合图形管线的通用并行计算任务。典型场景包括:物理模拟(流体、布料、粒子系统)、图像处理(后处理效果、滤波、图像识别)、AI 推理(神经网络前向传播)、数据并行算法(排序、搜索、加密)。
2.3.3 Ray Tracing Pipeline
Ray Tracing Pipeline 用于光线追踪渲染,通过模拟光线物理行为生成图像。现代 API(DirectX 12 Ultimate、Vulkan Ray Tracing)提供硬件加速支持。
核心概念
- Acceleration Structure(AS):用于加速光线-几何体求交测试的数据结构(如 BVH)。AS 构建与遍历通常由 GPU 硬件加速。
- Ray Generation Shader(RGS):生成初始光线,通常从摄像机位置发出。
- Closest Hit Shader(CHS):光线与场景几何体最近交点处执行,计算材质属性与光照。
- Any Hit Shader(AHS):可选,光线与几何体任何交点处执行,常用于透明度或剔除。
- Miss Shader(MS):光线未命中任何几何体时执行,处理背景或天空盒。
- Intersection Shader(IS):可选,处理非三角形图元(球体、曲线)的求交测试。
执行模型
RGS 生成光线后,光线在场景中遍历,与 AS 进行求交测试。命中时调用 CHS 或 AHS,未命中时调用 MS。该过程可递归进行,模拟光线多次反射与折射。
RT acceleration 将 BVH traversal 与 ray-box / ray-triangle intersection 移出通用 shader datapath,降低 intersection 测试的 ALU 成本。硬件加速未覆盖的瓶颈(traversal depth、memory locality、payload pressure、material divergence)在 4.10 节沿 RTU 路径展开。
光线追踪的应用场景包括:电影级离线渲染、实时光线追踪游戏中的光照/反射/阴影/全局照明、以及 VR/AR 中的真实视觉体验。
2.4 Command
应用程序在 CPU 上运行,通过 Graphics API 构建一系列指令,组织为 Command List 或 Command Buffer。这些指令块由 GPU 按序或按依赖关系执行。Driver 负责将 API 命令翻译为 GPU 原生指令格式。Command 可分为三类:State Command、Barrier Command 与 Action Command。
2.4.1 State Command
State Command 配置 GPU 内部状态,为后续 Action Command 设定执行环境。现代 API 强调状态管理效率,频繁状态切换是传统 API 的主要性能瓶颈。
Render Pass 与 Attachment 契约
Render Pass 是带宽契约(Bandwidth Contract),不只是状态容器。尤其在 TBR/TBDR 架构下,Render Pass 定义了渲染目标(Attachments)的像素数据生命周期,包括 LoadOp 与 StoreOp 行为。
- Attachments:渲染操作的目标,包括 Color Buffer、Depth Buffer 与 Stencil Buffer。
- LoadOp:定义 Render Pass 开始时附件的处理方式:LOAD(加载现有内容)、CLEAR(清除为预设值)、DONT_CARE(不关心初始内容,GPU 可选择不加载以节省带宽)。
- StoreOp:定义 Render Pass 结束时附件的处理方式:STORE(写入全局内存使后续操作可见)、DONT_CARE(不关心最终内容,可节省带宽)。
- Transient Attachments / Memoryless Attachments:内容仅在 Render Pass 内部有效,无需存储到全局内存。GPU 可将其完全保存在 Tile-local Storage 中,避免高带宽的全局内存访问。适用于延迟渲染中的 G-Buffer 等中间结果。
在 TBR 架构中,GPU 将屏幕划分为 Tiles,每个 Tile 的渲染在片上高速存储中完成。Render Pass 的附件契约允许 driver 与硬件精确知道哪些数据需在 Tile Memory 中保留、哪些可丢弃、哪些需写回全局内存。合理设置 LoadOp/StoreOp 与使用 Transient Attachments 可最大限度减少对全局内存的读写。
Pipeline State Object(PSO)
PSO 将大量分散设置的渲染或计算状态预先打包为不可变对象,包括 shader 代码、顶点输入布局、图元拓扑、光栅化状态、深度/模板测试状态、混合状态与渲染目标格式。
PSO 的优势:
- 减少驱动开销:传统 API 中每次 Draw Call 前需多次 API 调用设置状态。PSO 允许 driver 在创建时一次性编译、验证与优化。渲染循环中绑定 PSO 是轻量级操作。
- 提高可预测性:不可变性保证渲染状态一致性,减少运行时状态切换的复杂性。
- 支持多线程录制:PSO 包含大部分渲染状态,多 CPU 线程可并行录制 Command List。
PSO 类型包括 Graphics Pipeline State、Compute Pipeline State、Mesh Pipeline State 与 Ray Tracing Pipeline State。
PSO 创建是耗时操作,涉及 shader 编译与状态验证。最佳实践是预先创建并运行时复用,避免在渲染循环中动态创建。
Resource Binding
Resource Binding 指定当前激活的 shader 将访问哪些 GPU 资源。现代 API 机制:
- Descriptor:对 GPU 资源的轻量级引用,包含访问所需信息(内存地址、格式、维度)。
- Descriptor Heap/Set:GPU 可访问的内存区域,存储大量 Descriptor。
- Root Signature / Pipeline Layout:定义 shader 期望的资源绑定接口,指定从哪些 Binding Point 读取哪种类型资源。
绑定流程:在 Command List 中绑定包含所需 Descriptor 的 Descriptor Heap/Set,设置指向具体 Descriptor 的偏移量或索引。这种设计将资源绑定与 Draw/Dispatch 解耦,GPU 可更快地切换绑定。
- Push Constants / Root Constants:用于少量、频繁更新的数据,直接嵌入 command buffer 中,无需经过 Descriptor Heap。适用于每帧变化的矩阵、时间参数等。由于数据量有限(通常几百字节),不适合大规模资源绑定。
开发者需要仔细管理 Descriptor 的分配、更新与生命周期,确保 shader 访问时 Descriptor 有效且指向正确资源。Descriptor 驻留管理不善可能导致 GPU 访问无效内存或产生 page fault。
这套管理职责常被当作显式 API 的固有成本。以下判断与当前主流叙事相反:Descriptor Table 不是通往 Bindless 的过渡形态,而是高并发引擎中更负责任的默认选择。理由分三层。
【判断:Descriptor Table 不是通往 Bindless 的过渡形态,而是高并发引擎中更负责任的默认选择】
成因层。早期 GPU 没有通用逻辑寻址能力,shader 访问资源只能走固定的纹理寻址路径,资源引用必须封装为硬件可识别的描述符结构,由 CPU 侧准备好再切换给 GPU。高层 API 强迫开发者录制并动态切换巨型、不透明的 Descriptor Heap,本质是那个年代硬件寻址能力的代偿。Root Signature 与 Pipeline Layout 定义的绑定接口同样如此:它们是交给 shader 的一张寻址契约,声明哪组表项、哪种类型、哪个槽位,契约的存在前提依旧是硬件不认识指针。现代硬件早已实现平坦的原生 64-bit 虚拟内存寻址:GPUVA 提供连续地址空间,Buffer Device Address 让 shader 直接持有指针,资源访问在指令层退化为一次 Load Global(LDG)指针解引用。Push Constants 把高频小数据直接嵌入 command buffer,同样是绕开 descriptor 路径的证据。API 层的绑定动作,在硬件指令流里早已没有对应物。
代价层。Bindless 让 shader 摆脱固定槽位,经句柄或索引动态访问全量资源,CPU 侧的绑定开销随之消失。但指令层的退化不构成拥抱纯 Bindless 的充分理由。当 working set 规模过大,或访问模式出现不规则的稀疏索引时,发散的资源索引(divergent resource indexing)会剧烈破坏硬件 descriptor cache 的局部性,随之而来的是 TLB miss 与 page walk 的长尾延迟。一次不命中的间接寻址可能走到数百周期,形成可观察的零利用率停顿,在 profiler 上呈现为难以归因的长尾。Bindless 没有消除成本,它把成本从 CPU 侧转移到了 GPU 侧最不可预测的位置。Architecture 章沿 texture path 逐项展开这一分析。 取舍标准层。物理吞吐稳定性上,Descriptor Table 把资源集合的切换收敛为少量表项更新,表项内容可被硬件预取与驻留,stall 分布可控。RHI 调试确定性上,表项拥有明确的创建、绑定与失效语义,崩溃现场可以逐层回溯,这正对着 2.7 节指出的 RHI 调试复杂性。发散索引驱动的访问则把错误现场推入 TLB 与 page walk 深处,定位成本由引擎侧转嫁到驱动与硬件的交界。支持细粒度局部控制、避免无节制 indirection 的 Descriptor Table 模型,在现代高并发引擎设计中具有更稳定的工程收益。资源绑定正在退化为指针操作,图形 API 与 Compute API 在这一层的语义差异随之收窄。
Dynamic State
部分频繁变化且对性能影响较小的状态可设置为 Dynamic State,在 Command List 中动态设置而无需切换整个 PSO。典型 Dynamic State 包括 Viewport、Scissor Rect、Blend Constants、Depth Bounds、Stencil Reference。过度使用 Dynamic State 可能增加 GPU 运行时开销。
2.4.2 Barrier Command
Barrier Command 在并行与内存环境中强制实施顺序与可见性保证。Barrier 是 dependency contract,不等于单一 cache flush。其核心作用在于四个维度:
- Execution Dependency:确保屏障前特定 Pipeline Stage 的 GPU 工作完成后,屏障后特定工作才能在特定 Stage 开始。现代 API 允许精细指定 srcStageMask 与 dstStageMask 以最小化等待。
- Memory Availability:确保屏障前的内存写入操作在屏障后对后续操作"可用",数据处于正确的内存层级中可被访问。
- Memory Visibility:确保屏障前的内存写入对屏障后的读取可见,通常通过 cache flush 与 invalidate 实现。
- Resource State & Layout Transition:管理 GPU 资源在不同用途间切换时的内部状态同步。例如纹理从 Render Target 转为 Shader Resource 时,Barrier 在状态转换间插入同步点,涉及资源状态转换、内存布局转换与访问权限转换。
Driver 与硬件通过在 Barrier 处插入微码指令、调整内存控制器行为、执行缓存操作与管理资源元数据来满足上述同步语义。这些操作的具体形式因硬件架构而异:某些架构可能使用 scoreboard-style tracking,某些使用 dependency counter,某些使用更隐式的硬件机制。Barrier 的实现是多层同步机制的组合,远不止单一的 cache flush。
这一走向在 API 规范层已经有可观察的落点。Apple 官方 Metal 4 文档把 MTL4CommandEncoder 的 barrier 定义为 after(前置阶段)、before(后置阶段)、access(访问范围)三个逻辑参数:after 与 before 各取一组 MTLStages,限定屏障两侧受约束的流水线阶段。access(官方签名中为 visibilityOptions,MTL4VisibilityOptions)声明这次同步涉及的内存一致性范围。整组字段里没有旧式 API 那套 stage mask、access mask 与 layout 的组合,官方语义就是一条执行依赖:after 侧命令完成之前,before 侧命令不得开始。这与该节的判断一致:Barrier 在 API 层是 dependency contract,不是一次物理清洗动作。至于这份 contract 在旧硬件上为什么曾经昂贵,我留到 3.5 节的三层叙事里展开。
2.4.3 Action Command
Action Command 触发 GPU 执行实际渲染或计算任务。
Draw Command
- Draw():直接绘制 Vertex Buffer 中的几何体。
- DrawIndexed():通过 Index Buffer 绘制,允许复用顶点数据。
- DrawIndirect():从 GPU 内存缓冲区读取绘制参数,实现 GPU Driven 绘制。
Dispatch Command
- Dispatch():启动指定维度的 Workgroup Grid。
- DispatchIndirect():从 GPU 内存缓冲区读取调度参数。
Ray Tracing Command
- DispatchRays():启动光线追踪过程,指定光线数量与维度。
Action Command 的执行依赖 State Command 所配置的环境与资源绑定。Draw Command 触发 Raster Pipeline 的完整执行流程。Dispatch Command 触发 Compute Pipeline 的 Workgroup 调度。Ray Tracing Command 触发 BVH 遍历与光线-几何体求交。每种 Action Command 对应不同的硬件执行路径与内存访问模式,性能分析时需区分其瓶颈来源:Draw 受限于 vertex fetch、rasterization throughput 与 Pixel Backend。Dispatch 受限于 memory bandwidth、Local Shared Storage 使用与 occupancy。DispatchRays 受限于 traversal memory latency、intersection test throughput 与 shader divergence。
2.5 Queue
GPU 工作负载通过 Command Queue 管理与提交。Command Queue 是 FIFO 队列,应用将 Command List 提交到队列,GPU 按提交顺序执行其中的命令。现代 API 支持多个不同类型的 Queue,允许 GPU 在不同类型的任务之间实现并行执行。
队列类型
- Graphics Queue:提交图形渲染相关的 Command List,包括 Draw Command、Render Pass 与图形状态的 State Command。Graphics Queue 通常也支持计算与传输命令,是功能最完整的队列。
- Compute Queue:主要用于提交通用计算相关的 Command List,尤其是 Dispatch Command。Compute Queue 可与 Graphics Queue 并行执行,使 GPU 同时进行渲染与计算任务。这种异步计算(Async Compute)模式可在渲染管线空闲时填充计算工作,提高整体利用率。
- Transfer Queue:主要用于提交内存传输相关的 Command List,如数据上传、下载或复制操作。Transfer Queue 通常由专门的 Copy Engine 或 DMA Engine 处理,可在渲染与计算进行的同时执行数据搬运。Copy Engine 与 shader core 共享 memory bandwidth 与 fabric 资源,大规模数据搬运可能影响 shader core 的 memory latency 与 throughput。
异步执行与同步
不同类型的 Queue 可异步并行执行。例如 GPU 可在 Graphics Queue 执行渲染的同时,在 Compute Queue 执行计算或在 Transfer Queue 执行数据传输。这种异步执行减少 GPU 空闲时间,最大化利用率。
不同 Queue 之间的同步依赖通过 Fence、Semaphore 或 Event 协调。例如 Compute Queue 完成数据处理后发出 Semaphore 信号,Graphics Queue 等待该信号后使用处理结果进行渲染。同步机制确保跨队列的数据依赖被正确满足,同时允许不相关的工作并行推进。
2.6 Synchronization
GPU 编程中的同步确保并行执行的各部分正确协调,避免数据竞争。同步发生在 CPU 与 GPU 之间,以及 GPU 内部不同工作负载之间。
2.6.1 CPU-GPU Synchronization
CPU 与 GPU 异步执行,CPU 提交命令后立即返回,GPU 在后台执行。需要机制确保 CPU 在需要 GPU 结果时等待 GPU 完成。
Fence
CPU 向 Command List 中插入 Fence 信号后继续执行。GPU 完成 Fence 前所有命令后,Fence 状态变为 signaled。CPU 通过轮询或等待 Fence 阻塞自身。Fence 是最常用的 CPU-GPU 同步原语。
Event
Event 提供比 Fence 更细粒度的 CPU-GPU 同步。CPU 可在 Command List 任意位置插入 Event 信号并等待。GPU 执行到 Event 时将其状态设为 signaled。Event 适用于等待 GPU 完成特定操作而非整个 Command List 的场景。
Implicit Synchronization
某些情况下 API 自动插入隐式同步。例如 Present Command 前 driver 通常插入隐式同步确保渲染完成。隐式同步简化编程但可能引入不必要的性能开销。
硬件端落点
API 层面的 Fence/Barrier 最终由命令处理器(Command Processor, CP)翻译为硬件级同步序列。硬件 Fence 本质是一个可寻址的内存位置加一条比较规则:CP 写入 fence_value,后续指令携带 reference 与比较算子(等于、大于、位掩码匹配等),当条件满足时流水线才继续派发。硬件规格通常定义十余种 fence 类型,覆盖不同引擎与地址空间。
Vulkan vkCmdPipelineBarrier 在驱动层被拆解为三步硬件操作:① 向目标 fence 地址写入标记值(对应 srcStageMask 完成)。② 插入 wait 指令阻塞后续派发直到 fence 条件满足。③ 根据 dstStageMask 发起对应级别的 cache flush/invalidate。srcStage 与 dstStage 的编码决定了 drain pipe 的深度,即 CP 需要等待流水线排空到哪个阶段才释放后续命令。drain pipe 是一种更激进的同步:强制前序命令全部退出流水线,代价高但保证严格顺序。
SPIR-V 中的 OpControlBarrier/OpMemoryBarrier 携带五类语义参数,各自映射到硬件行为:Execution Scope 约束哪些执行单元参与同步(workgroup 内或跨 workgroup)。Memory Scope 决定 snoop/probe 的广播范围(L1 局部还是 L2 全局)。Storage Class 指定操作涉及的地址空间。Availability 语义触发 cache write-back(脏数据从私有 cache 推至共享层级)。Visibility 语义触发 cache invalidation(令目标 cache 行失效以拉取最新数据)。理解这组映射后,barrier 不再是黑盒,它是对缓存层级的精确操控。
Predicate 机制是另一类硬件级"条件同步":CP 读取先前 Occlusion Query 写入的结果寄存器,若样本数为零则跳过后续 Draw Call,整个判断在 CP 内完成,不打断流水线节奏。
2.6.2 GPU-GPU Synchronization
GPU 内部多个 Queue 可并行执行,Queue 之间以及同一 Queue 内不同 Command List 之间的数据依赖,需要显式同步原语协调。
Semaphore
一个 Queue 完成任务后发出信号,另一个 Queue 等待该信号,确保任务顺序执行。例如 Compute Queue 完成数据处理后发出信号,Graphics Queue 等待后使用处理结果。
Barrier
Barrier 在 GPU 内部建立执行依赖与内存可见性约束。可应用于不同粒度:Global Barrier(影响所有工作负载与内存访问)与局部 Barrier(影响特定管线阶段或内存区域)。
Event
Event 也可用于 GPU-GPU 同步,一个 Command List 在某点发出信号,另一个 Command List 在另一点等待,实现细粒度同步。
2.6.3 Synchronization Granularity & Overhead
同步粒度指同步操作影响的范围。粗粒度同步(等待整个 Command List 完成)编程简单但开销较大。细粒度同步(等待特定 shader 阶段完成)开销较小但编程复杂。
同步操作本身引入 CPU 端 API 调用开销、GPU 端硬件同步开销与等待时间。最小化开销的策略:减少不必要同步,仔细分析数据依赖关系只在必要时插入同步。使用最轻量级同步原语(GPU-GPU 同步优先使用 Semaphore 而非 Fence)。合并多个同步操作以减少 API 调用与硬件开销。利用 Pipeline Stage 的精确掩码缩小 Execution Dependency 范围,避免过度等待。
2.7 Render Hardware Interface(RHI)
RHI 是位于引擎/渲染器与底层 Graphics API 之间的抽象层,提供统一接口使上层渲染代码独立于特定 API,实现跨平台兼容性。
工程价值
- 跨平台抽象:允许编写一次渲染代码,在多个 API 与操作系统上运行。
- 降低开发复杂性:封装底层 API 复杂性,提供更简洁的高级接口。
- 模块化与可扩展性:图形后端可作为可插拔模块管理,便于支持新 API 或硬件。
代价
- 功能滞后:RHI 需要时间适配底层 API 的最新功能,新特性可能无法立即获得。
- CPU 开销:RHI 层引入额外抽象与函数调用,可能导致一定 CPU 开销。
- 调试复杂性:RHI 抽象层增加调试难度,开发者需同时理解 RHI 层与底层 API 行为。
RHI 的设计需要平衡抽象统一性与性能暴露度。过度统一的接口可能隐藏底层 API 的关键性能特征(如 barrier 的精确控制、memory type 的显式选择),导致上层应用无法充分利用硬件能力。好的 RHI 实现需要在保持跨平台一致性的同时,为性能敏感场景提供底层控制能力的逃逸通道(escape hatch)。
2.8 Feature 成本迁移分析
Bindless、GPU Driven / Indirect、Mesh Shader 等 Feature 不应仅按 API 收益描述。每个 Feature 降低某一侧成本的同时,会将压力转移到其他硬件路径。Feature 是否适合某个 workload,取决于成本迁移后落在哪条硬件路径,以及该路径当前是否已接近瓶颈。
Bindless
Bindless 减少 CPU binding overhead:传统 binding model 中 CPU 需管理 Descriptor Table 与执行 bind 调用,Bindless 消除这部分工作。但 GPU 侧需要在每次 sample/load 时动态查找 descriptor cache、fetch resource header、处理 TLB 与 page walk。working set 较大或 residency 管理不完善时,page fault 与 TLB miss 明显上升。
成本迁移:CPU binding overhead↓ → descriptor cache / texture header fetch / TLB / residency / page fault risk↑
GPU Driven / Indirect
GPU Driven 减少 CPU submission:draw/dispatch 的生成从 CPU 移到 GPU,减少 CPU submission 瓶颈。但间接参数需要 GPU 内部 command processor 解析 indirect buffer、处理 front-end dependency、等待 indirect argument 就绪、执行 visibility buffer compaction,引入新的 barrier 与 front-end 压力。
成本迁移:CPU submission↓ → command processor / front-end / indirect argument dependency / barrier / visibility buffer / compaction cost↑
Mesh Shader
Mesh Shader 减少固定几何管线约束:开发者在 Workgroup 内自由生成几何体,摆脱传统 IA→VS→HS/DS→GS 管线的固定结构。但 vertex reuse 不再由固定管线的 post-transform cache 自动完成,需手动管理。Mesh payload 占用 Local Shared Storage 与 register,export 阶段需将 amplified geometry 送入 rasterizer,增加 export bandwidth 压力。Mesh Workgroup 的 occupancy 受 payload size 与 Local Shared Storage 用量约束。
成本迁移:固定几何管线约束↓ → manual vertex reuse / payload / shared memory / register pressure / export bandwidth / workgroup occupancy cost↑
每个 Feature 都是对某条 hardware path 的重新映射,工程判断的核心在于成本迁移后的路径余量分析。
三、Graphics Driver
Graphics API 定义了应用程序与 GPU 交互的接口规范,但规范本身不产生硬件行为。将抽象契约转化为物理执行的,是 Graphics Driver。理解 Driver 的工作边界,需要首先明确一条基本原则:Driver 承担的工作远超 API 调用的简单中继,涵盖 command packet construction、resource state tracking、residency management、synchronization lowering,以及与 firmware / command processor 的交互。Compiler 同样不是简单的文本翻译器,它负责 register allocation、scalarization、vectorization、instruction scheduling、dependency placement、spill decision 和 descriptor access lowering。
从更宏观的视角看,Driver 的核心任务是将 Graphics API 所定义的抽象契约 (API Contract) 转化为能够在具体硬件上执行的物理实现。这个过程围绕四条主线展开:
Submission Contract(提交契约):定义应用程序如何向 GPU 提交工作,包括 command buffer 格式、queue 语义、提交顺序和同步原语。Driver 负责将应用程序的提交意图翻译为硬件可消费的 command stream。
Memory Contract(内存契约):定义资源的分配、映射、可见性和生命周期。Driver 负责管理 VRAM 与系统内存之间的数据迁移,维护 GPU MMU 的页表,处理 memory residency 和 paging。
Synchronization Contract(同步契约):定义操作之间的依赖关系和执行顺序。Driver 需要将 API 层的 barrier / fence / semaphore 映射为硬件层面的 cache operation、pipeline stall 或 queue ownership transfer。
Reliability & Isolation Contract(可靠性与隔离契约):定义多个应用程序共享同一 GPU 时的错误隔离、超时恢复和资源保护机制。Driver 通过 GPU context 隔离、TDR 错误恢复和抢占机制实现这一契约。
这四条主线构成了 Driver 设计的核心框架。Driver 围绕这四条契约构建翻译逻辑,而非围绕 API 函数逐个实现。
3.1 驱动程序的核心职责
驱动程序的职责远不止中继 API 调用。 应用程序通过 Graphics API 发起调用,Driver 接收后需要完成解析、验证、状态跟踪、资源翻译、命令构造和硬件提交。以 DrawTriangles 为例,同样的调用在不同 GPU 上会产生不同的 command packet 序列、不同的 pipeline state 布局和不同的参数格式。Driver 必须根据硬件架构构造正确的指令,涉及对硬件 register map、command processor 语义、firmware 接口的充分理解。
Shader 编译不是文本翻译。 应用程序提交的 HLSL/GLSL 源代码需要经过完整的 compiler pipeline 才能变成硬件可执行的 native binary。Compiler 的完整职责包括:
- Register allocation:将虚拟寄存器映射到物理 Vector RF 和 Scalar / Uniform RF,决定 live range、分配粒度和 spill 策略。Register count 直接影响 resident Logical Warp 数量和 occupancy
- Scalarization:将 wave/warp-uniform 计算从 SIMD datapath 迁移到 scalar path,减少 Vector RF 占用,降低 operand collection 压力
- Vectorization:重组数据布局以匹配 SIMD width,提升 lane utilization,减少 instruction count
- Instruction scheduling:重排指令顺序以隐藏固定延迟依赖(如 ALU-ALU 短延迟),减少 pipeline stall,提升 issue rate
- Waitcnt / dependency placement:在可变延迟依赖(memory return、texture fetch、export、Local Shared Storage access)前插入 wait instruction 或 dependency counter 控制。这是 compiler 与 hardware dependency tracking 机制之间的关键协作点
- Memory scope lowering:将 high-level memory ordering 语义(如 Vulkan memory scope)映射到具体 cache flush / invalidate / fence 操作
- Spill / scratch decision:当 register pressure 超过物理 RF 容量时,决定将哪些 live range 溢出到 scratch memory。Scratch 访问增加是可观察的性能退化信号,表现为 occupancy 下降和 memory traffic 上升
- Descriptor access lowering:将 high-level resource binding 翻译为硬件 Descriptor Table / resource header 访问路径
Compiler 的产出不是单一的二进制码。Native ISA 的完整组成不仅包含 opcode,还可能包含 predicate、dependency hint、reuse flag、wait instruction、barrier、memory scope、cache operator 等字段。这些组成部分共同决定了硬件实际执行的行为,也是 compiler 与 hardware 之间的关键契约。如果只把 native ISA 理解为 opcode 集合,会忽略 compiler 在依赖管理、资源分配和指令编排中的核心作用。
Command Buffer 未必是硬件最终可直接识别的指令码。 UMD 生成的 Command Buffer 更准确地说是一种 Hardware Command Stream 或其等价表示。它最终由 Command Processor (CP)、KMD、firmware 或它们的组合来解释和执行。不同平台的最低可见层级不同:PC GPU 更接近于一个 packet stream 直达 CP。移动和 SoC 领域则有大量控制面逻辑实现在 firmware 中,驱动的角色更偏向于与 firmware 交互。这一区分直接影响 GPU 性能分析:Command Buffer 中的指令数不等于硬件实际执行的周期数,中间还经过了解释、转换和可能的动态优化。
API 与 Driver 的耦合关系。 Graphics API 仅定义规范和函数签名,驱动 GPU 完成工作的是底层 Driver 实现。以 Windows 为例,Microsoft 提供 Direct3D API 规范和运行时,但 NVIDIA、AMD、Intel 等不同 GPU 的厂商驱动在后台实现了这些 API 调用。应用程序链接到操作系统提供的 Graphics API 库(如 d3d12.dll),这些库加载相应厂商的 UMD。UMD 向上提供符合 API 规范的接口,向下负责生成提交给内核的命令流。这种耦合关系意味着:API 行为的具体语义由 Driver 实现定义,而非仅由规范定义。同一个 API 调用在不同厂商 Driver 上可能产生细微差异,这是跨平台 GPU 开发中需要关注的工程现实。
3.2 用户模式与内核模式
现代操作系统将 Graphics Driver 划分为用户模式驱动 (UMD) 和内核模式驱动 (KMD) 两部分。这种分层将逻辑分别置于用户空间 (User Space) 和内核空间 (Kernel Space),以提升系统稳定性和安全性。UMD 崩溃只会影响当前应用程序,KMD 崩溃则可能导致系统蓝屏或死机。将复杂逻辑(如 Shader 编译)放在 UMD 中,是现代驱动架构的基本安全原则。
UMD 的职责
UMD 运行在用户空间,直接与 Graphics API 运行时交互。其职责包括:
API 调用解析与验证。 UMD 接收来自应用程序或 Graphics API 运行时的函数调用(创建 Buffer、编译 Shader、设置 Render State、发出 Draw Command 等),检查调用参数的有效性,确保其遵循 API 规范。对于 OpenGL 等 High-Level API,UMD 需要维护大量的状态机 (State Machine) 以模拟规范中定义的状态行为。
Shader 编译与优化。 UMD 调用 compiler 将 HLSL/GLSL 等 shader source 编译为 native ISA binary,执行前述的 register allocation、instruction scheduling、scalarization 等完整优化流程。编译后的 Shader 被加载到 GPU,供后续 Draw Call 使用。
Command Buffer 构建。 这是 UMD 的核心工作之一。UMD 根据应用程序的绘图调用,翻译生成对应的 Command Buffer 或 Command List。如前所述,UMD 生成的 Command Buffer 并非总是硬件能直接识别的最终指令码。更准确地说,它生成的是一种 Hardware Command Stream 或其等价表示。
与 KMD 通信。 UMD 无法直接访问硬件,需要通过操作系统提供的接口与 KMD 协作。UMD 将准备好的 Command Buffer、Shader 等数据通过系统调用或特定驱动接口提交给 KMD。在 Windows 平台,现代驱动通过 D3DKMTSubmitCommand 经由 DirectX Graphics Kernel Subsystem (Dxgkrnl.sys) 进入内核。尤其在 WDDM 3.2 推动的 User-Mode Work Submission 模式下,Doorbell 和 Hardware Queue 成为关键组件,优化了提交路径。DeviceIoControl 则更多被视为历史路径或底层调试手段。在 Linux 下,Mesa 等 UMD 通过 ioctl 与 DRM 内核驱动交互。
除上述"正面"职责外,UMD 在运行期还承担着一些不那么光彩但不可或缺的角色:
硬件 bug 补丁。 特定硬件 stepping 下某些指令组合会触发已知 errata。UMD 在命令流中插入 workaround(额外的 NOP、fence、或重新排列指令顺序)来规避这些硬件缺陷。同一份 shader 在不同 GPU revision 上可能产生截然不同的底层命令序列。
软件模拟缺失功能。 当硬件不原生支持某些 feature(如 Wireframe 模式),UMD 用 Geometry Shader 或 Compute Shader 在软件层面兜底实现,对应用程序完全透明。
Fixed-Function 退役补偿。 早期 API 中的部分固定功能已无硬件对应物。D3D9 时代的 Alpha Test、Fog 等功能在现代 GPU 上不再有专用硬件单元,UMD 在 Pixel Shader 尾部注入等效的 discard 或 blend 逻辑来维持向后兼容。
Benchmark 特调。 针对知名 benchmark 的特定绘制 pattern,UMD 可能替换 shader 变体或调整调度参数以提升得分。这是 GPU 厂商公开承认的做法,也是为什么 benchmark 结果不应被视为通用性能指标的原因之一。
KMD 的职责
KMD 运行在内核空间,拥有对硬件的直接访问权限。其职责包括:
内存管理与地址空间。 GPU 通常有专用的 VRAM,并可通过 PCIe 访问系统内存。KMD 负责为用户空间请求的资源分配 VRAM 或映射内存地址,维护 GPU MMU 配置,处理虚拟地址到物理 VRAM 地址的转换,以及在系统内存与 VRAM 间迁移数据。
命令调度与队列管理。 UMD 提交 Command Buffer 后,KMD 负责将其加入 GPU Command Queue 并调度执行。现代 GPU 支持多上下文和多队列并行,KMD 需为每个应用程序维护独立的 GPU Context,确保多个应用的命令不会互相干扰。调度包括决定何时将哪个应用的命令发送给 GPU 执行,以及在必要时进行上下文切换 (Context Switch)。抢占 (Preemption) 也是调度的重要部分:当 GPU 任务运行时间过长或更高优先级任务到来时,KMD 需要能中断当前 GPU 任务以切换执行。
硬件初始化与资源管理。 KMD 在系统启动或驱动加载时初始化 GPU 硬件,设置工作模式、初始化寄存器、加载 firmware microcode,管理显示输出和功耗状态。
中断处理与错误恢复。 GPU 完成任务或遇到错误时向 CPU 发出中断信号。KMD 注册中断处理程序,在收到 GPU interrupt 时执行相应处理。当 GPU 发生错误时,KMD 记录错误信息并尝试恢复,例如 Windows 下的 Timeout Detection and Recovery (TDR) 机制。
旧式与新式 Graphics API 中的差异
传统 Graphics API(如 OpenGL、DirectX 11)与现代 Low-Level API(如 Vulkan、DirectX 12)对 Driver 的职责划分差异很大。
在传统 API 中,Driver 承担了大量自动化管理工作:隐式状态管理、隐式内存管理、隐式同步、shader recompilation(运行时根据状态变化重新编译 shader)。这些自动化功能降低了开发复杂度,但也增加了 Driver 开销和不确定性。Driver 需要在运行时做大量推理工作,推测应用程序的真实意图。
现代 API 将这些职责转移给应用程序。开发者需要显式管理内存分配与 residency、显式插入 barrier 与同步、显式控制 pipeline state(通过 Pipeline State Object, PSO)。API 允许预先录制 Command Buffer,将大量状态设置和命令生成工作在渲染前完成,降低了运行时的 CPU 开销。Driver 层因此更加精简 (Thin),其角色从"推理者"转变为"执行者",验证应用程序提供的命令序列并将其提交给硬件,而非在运行时推导依赖关系和资源状态。这一转变的实质是控制与责任的重新分配,Driver 本身的复杂度并未降低:应用程序获得更精确的硬件控制,同时也承担了更多正确使用硬件的义务。
3.3 Command Submission 的硬件形态
CPU 与 GPU 之间的命令提交流程,其硬件实现形态是理解驱动工作原理的关键。
Ring Buffer / Queue / Hardware Queue
这三者描述了命令流在软件和硬件中的组织形式。Ring Buffer 是一种常见的软件实现,UMD 将命令写入一块循环使用的内存区域,KMD 读取并提交给硬件。Queue 是更抽象的概念,可以指代软件中的命令队列或硬件中的执行队列。Hardware Queue 则是 GPU 硬件中实际存在的、用于接收和调度命令的队列。驱动程序最终需要将命令放入 Hardware Queue。不同平台的最低可见层级不同,有的平台更接近 packet stream 直达 Command Processor。有的平台(特别是移动和 SoC 领域)有大量控制面逻辑实现在 firmware 中。
从 UMD 发出命令到 GPU 实际执行,完整的提交链路分为四层:
- Command Buffer:UMD 在用户态写入的原始命令流,包含 API 级别的 state setting、shader binary 引用和 draw/dispatch token。这是应用程序"意图"的最直接表达。
- DMA Buffer:KMD 从 Command Buffer 拷贝并收编的 GPU 可执行命令。在这一步,KMD 执行越界审查、patch 显存虚拟地址为物理地址,确保用户态命令不会越权访问其他进程的资源。
- Paging Buffer:KMD 在 DMA Buffer 之前插入的资源搬运命令。通过 Blit engine 或 Copy Queue 将当前 draw 所需的 texture、buffer 等资源从 system memory 搬入 VRAM,确保 GPU 执行时数据就绪。
- Ring Buffer:GPU 固定的硬件队列,以 head/tail 指针管理。KMD 将 DMA Buffer 地址写入 tail 位置,GPU 硬件从 head 自取执行。这就是上文所述 Hardware Queue 的物理实现。
当 GPU 在 2 秒内无响应时,Windows 的 TDR (Timeout Detection and Recovery) 机制介入:OS 判定 GPU hang,执行 GPU reset,驱动负责恢复上下文状态并重新提交未完成的命令。抢占粒度从粗到细依次为 DMA level、Draw level、Primitive level,粒度越细,切换延迟越低,但硬件需要保存和恢复的状态越多,复杂度越高。
Doorbell 机制
这是一种低延迟的 CPU-GPU 通信机制。当 UMD/KMD 将命令准备好后,它直接向一个 GPU 专属的、已映射到 CPU 地址空间的内存地址(Doorbell)写入特定值,而非通过中断等高成本操作通知 GPU。GPU 硬件持续监视该地址,一旦发现数值变化,就知道有新命令需要处理,从而从 Hardware Queue 中提取命令执行。这种方式避免了内核上下文切换,大幅降低了提交延迟。
Progress Fence / Timeline Semaphore
Fence 是简单的信号对象,GPU 完成某点工作后更新其状态,CPU 可以查询该状态来判断 GPU 进度。Timeline Semaphore 是 Fence 的演进,包含单调递增的计数值,CPU 和 GPU 都可以等待某个特定计数值,或是在完成工作后更新计数值。这允许更精细的基于进度的同步,而非简单的完成/未完成二元状态。
Interrupt / Completion 通知机制
Doorbell 用于"踢"GPU 开始工作,但 GPU 完成任务后需要反向通知 CPU。Interrupt 是最传统的方式,GPU 通过硬件中断线向 CPU 发送信号,触发内核中的中断处理程序。现代 GPU 也支持更轻量级的完成通知,例如向共享内存写入完成状态,CPU 通过轮询获取 GPU 状态,避免中断开销。
3.4 各平台驱动框架演进
Windows 平台:从 XDDM 到 WDDM
Windows 显卡驱动架构经历了从 XDDM (XP Display Driver Model) 到 WDDM (Windows Display Driver Model) 的重大演进。XDDM 下驱动代码大量运行在内核,稳定性和安全性问题突出。WDDM 的核心思想是将驱动拆分为 UMD 和 KMD,将复杂逻辑(如 Shader 编译)移至用户空间,实现了"精简 KMD"的设计,大幅提升了系统稳定性和多任务处理能力。WDDM 还引入了显存虚拟化、GPU 调度、超时检测与恢复 (TDR) 等现代特性。
Linux 平台:从早期 DRI 到当代 DRM/KMS
Linux 图形驱动架构的核心组件包括 Mesa 3D(用户态驱动集合)、DRI (Direct Rendering Infrastructure) 和 DRM (Direct Rendering Manager)。早期 DRI 允许应用程序直接访问硬件,但存在安全风险。现代 Linux 图形栈以 DRM 为内核中枢,负责内存管理、命令调度和模式设置(通过 Kernel Mode Setting, KMS)。Mesa 提供 OpenGL/Vulkan 等 API 的实现,与 DRM 通过 ioctl 通信,共同构成完整的驱动栈。
NVIDIA 在 Linux 上的策略较为特殊。除了社区逆向的 nouveau 驱动,NVIDIA 也发布官方闭源驱动。近年来推出的 Open Kernel Modules 采用 MIT/GPL 双许可,但这只覆盖内核驱动部分,仍需与闭源用户态组件配合工作,与 nouveau 是完全不同的两套方案。
Apple 平台:驱动与硬件的紧耦合
Apple 的 GPU 驱动架构与其硬件设计紧密耦合。Apple 的 SoC GPU(如 AGX 系列)采用 TBDR 架构,大量控制面逻辑实现在 firmware 中。驱动的角色更偏向于与 firmware 交互,而非直接构造 packet stream。Apple 通过 Metal API 和配套驱动栈实现了对 TBDR 特性的显式暴露(如 Tile Memory、Render Pass 的 load/store action),使开发者能够充分利用 TBDR 架构的带宽节省特性。这种软硬件紧耦合设计使得 Apple 平台在 driver overhead 和资源管理效率上具有优势,但也意味着 Apple GPU 的驱动模型与 PC GPU 有明显差异:Command Buffer 的最低可见层级更靠近 firmware 抽象层,而非 hardware packet stream。
3.5 Barrier 和 Flush 的三层叙事
同步和缓存管理是理解现代 GPU 性能的关键。过多的 Barrier 及其导致的 Flush/Stall 是 Vulkan/DX12 等显式 API 中常见的性能瓶颈。需要从三个层面剖析:
规范层 (Specification Level)
API 规范定义了 Barrier 的语义:它是一种执行和内存依赖关系的描述。规范只定义"什么必须在什么之后可见",不强制规定硬件如何实现这种可见性。一个写后读(Read-after-Write, RAW)依赖的 Barrier 确保后续读操作能看到之前写操作的结果。规范层的核心概念是 dependency:Execution Dependency 保证操作顺序,memory dependency 保证数据可见性。
实现层 (Implementation Level)
驱动和硬件为满足规范定义的依赖关系,可能采取多种手段:Cache Flush(将缓存内容写回主存)、Cache Invalidate(使缓存失效以强制从主存重新读取)、Pipeline Stall(暂停流水线等待前序操作完成)、Queue Ownership Transfer(在不同硬件队列间转移控制权)。驱动根据具体的依赖类型和硬件特性,选择开销最小的实现方式。同一个 Barrier 在不同硬件上可能产生完全不同的 cache operation 序列。
架构层 (Architecture Level)
不同 GPU 对 Barrier 的硬件映射差异很大。一些架构拥有非常精细的缓存控制指令,允许驱动仅对特定缓存层级或数据范围进行操作。另一些架构只有全局性的 Flush/Invalidate 指令,导致 Barrier 开销较大。现代 GPU 倾向于提供更细粒度的控制(如 per-cache-level flush、partial invalidate、scoped memory operation),以降低不必要的同步开销。
三层之间存在明确的分离:规范层只定义依赖语义,实现层决定用哪些 cache operation 满足语义,架构层决定这些 operation 的硬件代价。同一个 Vulkan Barrier 在架构 A 上可能是轻量的 cache scope 操作,在架构 B 上可能是全局 pipeline stall。这是跨平台性能调优中 Barrier placement 成为核心课题的根本原因。开发者的目标是在保证正确性的前提下,将 Barrier 的架构层代价降至最低,而非试图消除 Barrier。
三层叙事解释了 Barrier 今天的形态:规范语义是什么、由谁实现、硬件代价多少。它没有解释 Barrier 为什么曾经如此昂贵,也没有回答它正在退化成什么。这两个问题的答案在硅片上。
【判断:Barrier 的物理依据正在撤离,屏障语义将退化为纯粹的执行序约束】
Vulkan Pipeline Barrier 需要给出 srcStageMask、dstStageMask、srcAccessMask、dstAccessMask、oldLayout、newLayout 一整组字段。参数如此繁琐,不是规范设计者的偏好,而是旧硬件上屏障的物理本质:那是一个高代价的物理清洗动作。Pixel Backend 邻接的 L2 slice 必须把 render target 数据连同 compression metadata 刷回显存,Texture Cache 必须整片失效,后续采样才能读到正确内容。oldLayout 与 newLayout 这对字段服务同一个目的:纹理在 render target 与 shader resource 两种用途之间切换时,硬件要重写寻址方式与压缩状态,数据必须先回到显存,再以新 layout 重新解读。开发者填写的每个字段都在为这次清洗划定范围:范围划大,清洗升级为整条管线的全局停顿;范围划小,代价是读到过期数据。Barrier 的复杂度,全部来自物理代价的精细化分摊。
这套物理前提正在消失。现代 GPU 的末级缓存是全片共享的全局一致点:NVIDIA 的巨型 L2 覆盖整片,Apple 在 SoC 级别部署 SLC(System Level Cache),在 L1 一致性被刻意弱化的前提下,跨阶段可见性由末级缓存保证。UTC、DCC 这类端到端的压缩保持机制让压缩状态由 metadata 全程携带,数据跨 render pass、跨 queue 流转时不再需要经历解压、回写与失效这一整套动作。数据在逻辑上根本没有移动,Layout transition 这个屏障存在的前提条件,退化为一次 metadata 状态更新,不再伴随数据搬运。
放回三层叙事的框架中:规范层的 dependency 语义是三层中唯一稳定的锚点;实现层可选的 cache operation 正在收窄,flush 与 invalidate 的触发频率持续下降;架构层的代价趋近于零。开发者今天积累的 stage 组合与 layout 选择经验,相当部分是旧清洗动作的残余知识,其失效时间已经可以预期。
精简于是成为必然。Metal 4 已经把 Barrier 退化为纯粹的流水线执行序依赖(Execution Dependency),只剩 after(前置阶段)、before(后置阶段)、access(访问范围)三个逻辑参数,取代了整组 stage、access、layout 掩码。屏障复杂度的历史成因是物理清洗动作,随着一致性由末级缓存保证,图形管线特有的屏障语义失去存在依据,图形与计算在同步语义上的边界随之消失。Barrier 与 Descriptor 的硬件依据正在同时撤离,图形 API 的特殊语义随之失去支撑,这一判断的完整论证需要逐厂商的物理证据,将在后续厂商篇中展开。
3.6 总结
Graphics Driver 是 API Contract 与 Hardware Execution 之间的翻译层。Compiler 同样不是简单的文本翻译器,两者共同完成从抽象契约到物理指令的转化。
从应用程序的 API 调用到硬件的实际执行,翻译经过多层。API 契约层是开发者的视角:stage、resource binding、barrier、render pass。Runtime / Driver 层把这些抽象翻译为对象、状态跟踪、command packet、residency management 与 synchronization lowering。Compiler 层在此基础上执行 register allocation、instruction scheduling、scalarization、vectorization、dependency placement、spill decision 与 descriptor access lowering。Compiler 产出的 Native ISA 不只是 opcode,还包含 predicate、dependency hint、reuse flag、wait instruction、memory scope、cache operator 等控制字段。最终 Hardware 层完成 command processor 解析、front-end 分发、execution、memory transaction 与 dependency tracking。
理解 Driver 的工作原理,特别是 command submission 的硬件形态(Ring Buffer、Doorbell、Fence)、memory residency 管理、barrier 从规范语义到硬件 cache operation 的三层映射,是分析 GPU 性能行为的必要基础。API 向更精简的 Low-Level 方向演进,Driver 的角色从运行时推理者转变为验证与提交的执行者,应用程序承担了更多显式控制职责。应用程序获得更精确的硬件控制,同时也承担更多正确使用硬件的义务。驱动设计的好坏,直接决定了这一翻译链的效率和稳定性。
四、Architecture
API 契约与 Driver 翻译层产出的 command stream、native ISA 与 barrier 序列,最终由 GPU 硬件执行。进入芯片内部,分析沿两条物理路径组织:memory request 从产生到返回的数据移动,draw/dispatch 从 command processor 到执行单元的 work distribution。内存子系统、Macro-Core、互连、调度、几何与光栅化,每个模块都要回答同一个问题:瓶颈在哪里形成。
4.1 Overview
GPU 的设计哲学围绕两个核心展开:吞吐量优先(Throughput-Oriented) 与 延迟容忍(Latency Tolerance)。二者贯穿在硬件组织、调度机制、内存层次和编程模型中,体现为具体的工程取舍。
大规模并行与单线程性能的取舍。CPU 通常集成 4~16 个复杂核心,每个核心配备深度流水线、乱序执行、分支预测等机制,目标是降低单线程延迟。GPU 在芯片面积约束下走向另一条路径:集成数以千计的计算通道,每个通道的控制逻辑精简到最低限度。当 GPU 将一个 Macro-Core(NVIDIA 称 SM、AMD 称 CU/WGP)的晶体管预算与 CPU 核心对比时,前者选择了"更多执行单元 + 更轻控制",后者选择了"更少单元 + 更重控制"。GPU 在高度并行 workload 下能达到远高于 CPU 的峰值吞吐率,代价是单个 invocation 的执行延迟更高,且完成时间不可预测。
延迟容忍而非延迟避免。CPU 依赖多级缓存、分支预测和乱序执行来避免或减少延迟暴露。GPU 接受显存访问数百个时钟周期的事实,不追求单次访问的延迟最小化,而是通过硬件多线程调度维持计算单元持续工作。一个 Logical Warp(NVIDIA 称 Warp、AMD 称 Wavefront、Apple/Intel 称 SIMD-group / subgroup)因等待数据返回而停滞时,调度器迅速切换到另一个就绪的 Logical Warp 继续执行。这种 Stall Swap 机制要求 Macro-Core 内同时驻留足够多的 Logical Warp,以提供足够的延迟隐藏候选。
简化控制换取执行规模。GPU 的调度器通常以一个 Logical Warp 为单位广播指令,Warp 内的多个 Vector Lane 锁步执行同一条指令。这种 SPMD / SIMT-like programming model 配合 SIMD datapath 的执行方式,大幅降低了指令调度和控制逻辑的硬件开销。代价在于,Logical Warp 内不同 lane 走上不同分支路径时,硬件需要通过 active mask / predicate / EXEC mask 等机制逐路径串行化执行,非当前路径的 lane 被暂时屏蔽。这种分支发散(Divergence)不会将 invocation 变成独立 CPU thread,但会导致 register live range 扩大、eligible warp 比例下降,从而降低有效利用率。
GPU 与 CPU 差异
核心数量与复杂度。CPU 核心数量少,每个核心包含复杂的控制电路,目标是最大化单线程执行速度和响应性。GPU 拥有数量庞大的简单计算通道,这些通道舍弃了复杂的控制电路以节省硅面积和功耗,从而在单芯片上堆叠更多执行单元。
并行执行与调度。CPU 主要依赖操作系统进行线程/进程调度,线程切换开销较高。GPU 在硬件中实现大规模线程调度,一个 GPU 可以同时跟踪数万甚至上百万个并发 invocation。这些 invocation 是轻量级的,切换时无需保存完整的寄存器上下文,可在硬件支持下快速完成。同一 Logical Warp 内的 invocation 锁步执行统一指令,这大幅降低了控制开销。
内存架构与带宽。CPU 依赖多级缓存层次(L1/L2/L3)来隐藏内存延迟,追求低延迟存取。主存通常为 DDR 内存,重视低时延和高随机访问性能。GPU 配备专用的高带宽显存(GDDR6、HBM 等),通过宽总线和高频率提供数百 GB/s 甚至上 TB/s 的内存带宽。GPU 的片上缓存层级较少(通常只有 L1 和 L2),容量相对较小,主要用于提升数据局部性。
指令执行与控制流。CPU 核心具备复杂的指令级并行(ILP)机制,同一时间可发射多条指令,并通过乱序执行动态重排指令顺序。GPU 采用顺序发射、锁步执行模型。当遇到条件分支时,CPU 依赖预测和投机执行,GPU 则采用 active mask 技术将各分支路径串行执行。
现代 GPU 层级结构
从芯片到 invocation 的层级结构如下:
Device / GPU Die
→ Shader Array / Cluster(NVIDIA 称 GPC、AMD 称 Shader Engine)
→ Macro-Core(NVIDIA 称 SM、AMD 称 CU/WGP、Intel 称 Xe Core)
→ Sub-Core / SIMD Partition
→ Logical Warp(NVIDIA 称 Warp、AMD 称 Wavefront、Apple/Intel 称 SIMD-group / subgroup)
→ Vector Lane
→ Invocation / Thread
Shader Array / Cluster 是 GPU 芯片中多个执行核心与前后端资源的组织层。一个完整的 GPU 芯片包含多个 Shader Array,每个 Array 内部包含若干 Macro-Core。Shader Array 还可能邻接固定功能的前端(几何处理)和后端(光栅化、像素输出)资源。通过调整 Shader Array 数量和每 Array 内的 Macro-Core 数量,GPU 制造商可以按需调整性能定位。
Macro-Core 是可驻留 Workgroup / Logical Warp 的主要执行单元。它包含执行实际计算的 ALU/FPU/SFU、Vector RF、Warp Scheduler、L1 Cache / Local Shared Storage 等。Macro-Core 是 GPU 并行计算的基本组织单元,芯片上的 Macro-Core 数量直接决定了 GPU 的并行吞吐上限。
Sub-Core / SIMD Partition 是 Macro-Core 内部的 SIMD 执行分区。一个 Macro-Core 可能包含多个 Sub-Core,每个 Sub-Core 拥有独立的 instruction buffer、调度器和执行 datapath。
Logical Warp 是调度与 masked execution 的基本粒度。一个 Logical Warp 包含固定数量的 Vector Lane(常见为 16、32 或 64 个),这些 lane 被约束为锁步执行。Logical Warp 是依赖跟踪、发射仲裁和延迟隐藏的基本单位。
Invocation 是编程模型中的逻辑执行实例。开发者编写 shader 或 kernel 时描述的是单个 invocation 的行为,硬件与编译器将多个 invocation 组织成 Logical Warp 在 SIMD datapath 上执行。
该层级结构是跨厂商分析的基础框架。不同厂商的具体实现存在差异,但层级关系本身稳定。
4.2 Memory Subsystem
GPU 内存子系统的核心任务是在数千并发 invocation 同时产生内存请求的环境下,维持足够的数据供给带宽并最小化请求往返延迟对执行效率的侵蚀。"大容量存储"只是附带结果。分析 GPU 内存子系统不应从 DRAM/SRAM/Flip-Flop 的材料学分类展开,应从 transaction path 视角切入:一个内存请求从产生到返回,经过哪些阶段,经历哪些合并与过滤,在哪些位置可能形成 stall。
Transaction Path 分析框架
每条 memory path 需要回答五个问题:
- 地址从哪来:由哪类指令或硬件单元产生地址。
- 请求如何合并:active mask 过滤、coalescing 粒度、sector / cache line 选择。
- 经过哪些 cache / TLB / metadata:路径上的缓存层级、TLB 查找、compression metadata 参与。
- 返回后如何解除依赖:return data 写回哪里、dependency tracking 如何清除、Logical Warp 如何恢复 eligible。
- 什么情况下造成 stall:cache miss、TLB miss、bank conflict、queue overflow、fabric backpressure、page walk。
Active Mask Filtering
在 coalescing 之前,硬件必须先根据当前 active lane mask 过滤掉非活跃 lane 的地址请求。未过滤的无效地址会形成无效 transaction,浪费 cache line 与带宽。
该步骤在不同厂商中的表现形式:NVIDIA 通过 active mask 与 predicate 共同决定哪些 lane 参与 memory request 形成。AMD 通过 EXEC mask 过滤 VMEM/SMEM/LDS 请求集合。mobile GPU 中同样存在但通常不对外暴露。active mask filtering 是 request formation 的不可省略的前置步骤。
Coalescing 与 Request Formation
当一个 Logical Warp 内的多个 lane 同时产生内存访问请求时,硬件尝试将这些请求合并为尽可能少的 cache line / sector 级 transaction。Coalescing 的前提是被访问地址在物理内存中连续或邻近,使得多个 lane 的数据可落入同一 cache line。如果 lane 访问模式分散,需要多个 cache line 才能覆盖,硬件不得不进行多次独立内存访问,实际带宽利用率大幅下降。
Fabric Arbitration
GPU 内部的 memory request 在到达 memory controller 之前,通常需要经过 interconnect fabric 的仲裁与路由。Fabric 层负责在多核、多 partition、多 cache 出口之间分配带宽与路径。当多个 Macro-Core 同时发起高带宽请求时,fabric arbitration 会成为瓶颈,表现为 memory latency 上升与 throughput 抖动。Fabric arbitration 是 memory path 中不可省略的中间步骤。
八条 Memory Path
Global LD/ST Path
地址来源:shader 中的 load/store 指令、硬件单元的显存访问请求。Macro-Core 内的 Address Generation Unit(AGU)计算每个 active lane 的物理地址。
请求合并:active lane mask 过滤后, surviving lane 的地址进入 coalescing 逻辑。Coalescing 以 cache line / sector 为单位合并相邻地址。32 个 active lane 各访问 4 字节相邻数据时,可合并为一个 128 字节 cache line 请求。
Cache / TLB 路径:L1 Data Cache(per-Macro-Core)→ TLB lookup(per-Macro-Core,miss 触发 page walk)→ L2 Cache(全片共享,按 slice 分布)→ memory controller。部分架构在 L1 和 L2 之间引入额外缓存层级。
依赖解除:数据返回后写入 Load Data Buffer / Result Queue,Dependency Tracking 机制清除对应 Logical Warp 的 memory return dependency,该 Logical Warp 恢复 eligible 状态,可被 scheduler 选中发射后续指令。
Stall 场景:L1 miss 且 L2 miss、TLB miss 触发 page walk、coalescing 失败导致大量独立 transaction、fabric congestion 导致 request queue overflow。
Texture / Sampler Path
Texture path 不等于 Global LD/ST path。Texture path 涉及专用的 addressing mode、filtering 机制和额外硬件阶段。
完整路径:
shader sample instruction
→ descriptor / resource header lookup
→ sampler state fetch
→ coordinate transform / LOD calculation / anisotropy selection
→ texture address generation
→ Texture Cache lookup
→ block compression decode(BCn / ASTC 等)
→ TLB / page walk(miss 时)
→ L2 / memory fetch
→ filter 计算(bilinear / trilinear / anisotropic)
→ return to shader
地址来源:shader 中 texture sample 指令携带的纹理坐标和 sampler index。纹理坐标经过 wrap/clamp/mirror 模式变换后,结合 LOD 和 mipmap 基址计算物理地址。
Descriptor Path 与 Bindless 代价:传统 binding model 中,descriptor 在 draw/dispatch 前由 CPU 绑定到固定槽位,sampler state 和 resource header 在绑定阶段已准备就绪。Bindless 每次 sample 需动态 lookup descriptor cache、fetch resource header,working set 超出 cache 容量时代价上升(沿 texture path 的完整分析见 4.9 节)。
依赖解除:filtered texel 数据返回 shader,写入 Vector RF,Dependency Tracking 清除 texture return dependency。
Stall 场景:Texture Cache miss 率高、sampler bottleneck(高各向异性过滤等级增加 texel fetch 数量)、descriptor cache miss、TLB miss、page walk、texture pipe busy 但 ALU idle。
Constant / Uniform Path
Shader 中引用 uniform/constant buffer 时,同一 Logical Warp 内所有 lane 通常访问同一地址,硬件通过 dedicated broadcast path 广播到所有 lane,由 per-Macro-Core Constant Cache 服务。miss 时回退到 L2 → memory。Stall 场景:Constant Cache miss(uniform/constant buffer 过大时)。
Local Shared Storage Path
Shader 显式访问 Local Shared Storage(LDS / Shared Memory / SLM / threadgroup memory)时,地址由 shader 计算,属于 workgroup 地址空间。路径为 LSU → 片上 SRAM(per-Macro-Core),访问延迟约 20~30 个时钟周期。存在 bank 组织,32 个 lane 同时访问时若多个地址映射到同一 bank 则产生 bank conflict,硬件需分拍仲裁。AMD 风格中通过 lgkmcnt 追踪 LDS 操作完成状态。Stall 场景:bank conflict、access pattern 不均匀导致热点 bank、LDS 容量超限。
Tile-local Storage Path
图形渲染中 render pass attachment(color/depth/stencil)的 tile 级读写走 Tile-local Storage,属于 pixel backend 附近的片上 SRAM。在 TBDR/TBR 架构中,整个 render pass 的 attachment 数据在此原位完成 blend、depth test、MSAA resolve,render pass 结束才一次性写回显存;IMR 架构中部分 GPU 引入 tile caching 优化,但 final pixel state ownership 仍归属 global framebuffer。Tile-local Storage 与 Local Shared Storage 都属于片上 SRAM 类资源,是否物理复用取决于厂商与代际(Apple Metal 通过 threadgroup_imageblock 暴露,Adreno 通过 tile memory heap 显式分配)。Stall 场景:tile memory pressure(attachment 过多或 tile size 过大导致超限)、cross-tile dependency 强制 flush/reload、render pass split 增加 store/load transaction。
Pixel Backend Write Path
地址来源:fragment shader 的 export/color output、depth/stencil backend 的 write-back。
Color / Depth 路径:fragment export → pixel backend / RB / ROP → blend → compression metadata 评估(DCC / delta color compression / fast clear metadata)→ compressed/uncompressed write → L2 / memory。depth 侧为 rasterizer / early-z output → Hi-Z / hierarchical depth test → depth/stencil backend → HTILE metadata → L2 / memory。完整代码块与 PS/CS 写路径对比见 4.8 节。
依赖解除:pixel backend write 是管线的最终阶段之一,不解除到特定 Logical Warp 的 dependency,而是完成 frame buffer 状态的持久化。后续 render pass 的 read 通过 render pass dependency / barrier 保证可见性。
Stall 场景:ROP/RB busy、blend 导致 serialization pressure、compression metadata 评估增加延迟、depth/stencil busy、bandwidth tax 上升。
RT Traversal Memory Path
Ray tracing acceleration structure traversal 指令发起 BVH node fetch,请求发起者是 Traversal Unit 而非 LSU。路径:Traversal Unit → BVH node fetch(Global Memory / L2 / dedicated BVH cache)→ ray-box / ray-triangle test → next node / hit result → return to shader。返回结果可能触发新的 shader invocation(hit shader),形成新的 dependency chain。Traversal 过程中 ray state 占用寄存器,形成 payload register pressure。Stall 场景:BVH node fetch miss(随机访问模式)、traversal length 变化、any-hit shader 引入 material divergence、payload register pressure 导致 occupancy 下降。
Matrix Operand Movement Path
Matrix/tensor 运算指令的 operand 通常从 register 或 shared memory 直接读取,不经过常规 LSU,走专用 accumulator / tensor datapath 完成精度/scale metadata 处理后写回。Stall 场景:operand layout 不符合 hardware tile shape 要求需重组、shared memory bandwidth 竞争、accumulator 资源争用。
Observable Stall:Memory Subsystem
| Stall 类型 | 可观察症状 |
|---|---|
| Cache miss | L1/L2 hit rate 下降,memory dependency stall 上升 |
| TLB miss | TLB miss counter 上升,page walk 延迟增加 |
| Coalescing 失败 | Transaction count 相对于 access count 比例上升,bandwidth utilization 下降 |
| Fabric congestion | Memory latency 上升且抖动增大,throughput 不平滑 |
| Bank conflict(LDS) | LDS bank conflict counter 上升,operand collector stall |
| Page walk | Page walk 计数器上升,memory latency 出现长尾 |
| Compression fallback | Metadata traffic 增加,uncompressed write 比例上升 |
4.3 Macro-Core 宏观视图
现代 GPU 的核心计算能力源于 Macro-Core。Macro-Core 在不同厂商中有不同名称(NVIDIA 称 SM、AMD 称 CU/WGP、Intel 称 Xe Core、mobile GPU 称 Shader Core),但它们承担相似的职能:驻留 Workgroup / Logical Warp,提供执行 datapath、寄存器文件、本地存储和调度逻辑。
层级组织
Shader Array → Macro-Core → Sub-Core 的组织、命名与各级职能已在 4.1 节展开。Macro-Core 内所有 Sub-Core 共享 L1 Cache / Local Shared Storage 和一部分调度资源。
公共组成部分
尽管具体实现各异,一个典型的 Macro-Core 通常包含以下组件:
执行 datapath(ALU/FPU/SFU/矩阵加速单元):执行实际整数、浮点和特殊函数运算。现代 GPU 还配备矩阵/张量加速单元,用于加速矩阵乘法。这些单元与通用 ALU/FPU 在 datapath 上分离,拥有独立的 issue port 和 accumulator。
Vector RF:为每个 Vector Lane 提供私有的、超高速本地存储,用于存放 per-lane 变量和中间结果。Vector RF 的组织方式受限于 SRAM 端口约束:一个 32-lane SIMD group 的一条指令需要同时读取 64~96 个操作数,全端口 RF 在面积上不可行,因此必须组织为多 bank 结构。
Scalar / Uniform RF:存放 Logical Warp 级别的 uniform 数据,如基址、常量、控制状态。AMD 将其显式实现为 SGPR(Scalar General-Purpose Register),NVIDIA 的 uniform data 走不同路径但承担类似角色。
Predicate / Mask Register:控制 active lane enable/disable,用于分支发散处理、predicated execution 和 reconvergence。
L1 Cache / Local Shared Storage:Macro-Core 内的片上高速存储。部分容量由硬件作为 L1 data cache 管理,部分可由 programmer 通过 API 显式分配为 Local Shared Storage(Shared Memory / LDS / SLM)。二者从同一物理 SRAM 池分配,但管理主体、访问语义和生命周期不同。另外,L1 Instruction Cache(L1-i)和 Constant Cache(L1-c)也是 Macro-Core 的常备组件。
Warp Scheduler / 依赖跟踪逻辑:管理和调度 Macro-Core 上执行的 Logical Warp。当一个 Logical Warp 因等待数据而停滞时,调度器切换到另一个就绪的 Logical Warp。Dependency Tracking 的具体实现因厂商而异:NVIDIA 依赖 hardware-managed scoreboard 判断 issue eligibility。AMD 通过 compiler 插入的 waitcnt 配合 hardware dependency counter 判断。Apple/mobile GPU 的实现通常不对外公开。
与其他模块的协作
Macro-Core 与 GPU 其他关键模块通过 interconnect fabric 连接:
- 与 Texture / Sampler Unit 的协作:shader sample 指令将请求发往 Texture Unit,由后者完成 address generation、cache lookup、filtering 后返回结果。
- 与 L2 Cache / Memory Controller 的协作:Macro-Core 的显存访问请求通过 fabric 路由到 L2 Cache slice 和对应的 memory controller。
- 与 Pixel Backend 的协作:fragment shader 的 color/depth export 通过 dedicated path 发往 pixel backend,不经过 Global LD/ST path。
4.4 Memory Management and Access
GPU 的内存管理与访问机制覆盖了从物理连接接口到虚拟地址转换的全部环节。分析这些组件需要追踪 memory request 从虚拟地址产生到物理 transaction 完成的完整路径,而非停留在功能介绍层面。
Memory Interface
Memory Interface 是 GPU 芯片与外部 Device Memory 之间的物理连接。GPU 为实现高内存带宽,采用宽总线与高速存储器结合的设计。
宽位宽与多通道。典型独立 GPU 具有 256-bit 至 384-bit 甚至更宽的总线,由多个独立内存通道并行工作。例如 384-bit 总线可视为 12 个 32-bit 通道,每个通道连接到一组 Device Memory 芯片或 HBM 堆栈的一部分。多通道设计允许 GPU 同时向多个 DRAM bank 发出读写请求。
高速信号与新型存储。GDDR6 有效数据速率通常在 14~19 Gbps,GDDR6X 采用 PAM4 信令提升至 19~21 Gbps。HBM 通过 3D 堆叠和硅中介层提供 1024-bit 以上总线宽度。这些技术使 GPU Device Memory 带宽远高于 CPU DDR 内存。
内存时序优化。DRAM 存在行预充电、激活等时序约束。MCU 采用 FR-FCFS(First Ready - First Come First Serve)调度策略,优先执行命中已打开行的请求,增大行命中率,减少行切换开销。
Memory Controller Unit (MCU)
MCU 位于 GPU 芯片与 Device Memory 之间,直接控制 DRAM 的读写时序,调度多个请求并管理底层 DRAM 协议。
多控制器并行。GPU 内部集成多个 MCU,每个 MCU 负责一组 Device Memory 通道。每个 MCU 具有独立的请求队列和调度逻辑,可独立处理其通道请求。GPU 根据地址映射将访存请求分散到不同 MCU,实现负载均衡。
地址映射与 Bank 利用。MCU 决定 Device Memory 地址到内部 row、column、bunk、rank 的映射策略。合理的映射将并发访问分散到不同 DRAM bank,避免热点。GPU 通常采用地址交织策略,使连续地址落在不同物理 bank 上。
请求队列与调度。每个 MCU 维护请求队列,处理来自 Macro-Core 和其他单元的内存请求。FR-FCFS 调度器优先处理已打开行的请求,其次按先来顺序处理。调度器还考虑读写切换延迟、刷新操作和不同来源请求的优先级。
与固定功能单元的物理布局。MCU 模块常与 L2 Cache slice、pixel backend 单元在物理上邻接。每个 MCU 附带的 L2 Cache 只缓存该 MCU 负责地址范围的数据,这种分区存储结构简化了 tile-based 或 partition-based 的一致性维护。
Memory Management Unit (MMU)
现代 GPU 具备 MMU,支持虚拟内存和地址翻译。MMU 允许 GPU 使用虚拟地址访问内存,由硬件和系统软件转换为物理地址。
虚拟地址空间。GPU 为每个进程上下文提供独立的虚拟地址空间,确保不同应用的内存访问不会混淆。支持 Unified Memory 的平台上,GPU 与 CPU 共享统一虚拟地址空间,GPU MMU 与 CPU MMU 协同工作,通过 PCIe ATS 或 NVLink 页表映射协议实现地址翻译。
页表与 TLB。GPU MMU 使用多级页表管理虚拟到物理地址的映射。页大小通常为 4KB,也支持 64KB 或 2MB 大页。GPU TLB 需要支持高并发查询:数百个线程可能同时访问内存,因此 TLB 通常具备多端口、多并发查询能力,并在 Macro-Core 内按 Logical Warp 级进行缓存。一些架构引入共享的 L2 TLB。TLB miss 触发 page walk,page walk 的延迟远高于 cache miss,是 memory path 上最昂贵的操作之一。
上下文切换与保护。MMU 支持多任务和上下文切换,通过 ASID(Address Space ID)标记区分不同进程的 TLB 条目,减少上下文切换时的 TLB 刷新。IOMMU 参与保护,防止 GPU 访问未授权的主机内存区域。
Direct Memory Access (DMA)
DMA 引擎能够在不经过 GPU 着色或计算核心干预的情况下,在不同存储区域之间搬运数据。
复制队列与异步执行。GPU 提供独立的 DMA 硬件队列(Copy Engine / Transfer Queue)。开发者将内存拷贝命令提交到 DMA 队列,由 DMA 引擎执行,同时计算引擎可进行其他计算工作,实现传输与计算并行。典型案例:GPU 渲染当前帧时,DMA 将下一帧所需数据从 CPU 内存预取到 GPU Device Memory。
PCIe/NVLink 总线传输。GPU 与主机内存交换数据通过 PCIe 或 NVLink。PCIe 4.0 x16 约为 32 GB/s,PCIe 5.0 可达 64 GB/s,远低于 GPU 内部 Device Memory 带宽(数百 GB/s)。NVLink 等专用互连提供更高的 GPU 间直连带宽,支持 P2P 数据传输。
零拷贝与 Pinned Memory。Pinned Memory 置于系统内存的页锁定区域,GPU 通过 DMA 直接读写,无需先拷贝到 Device Memory。适用于 CPU 和 GPU 频繁共享小量数据的场景。大量数据交互时,异步 DMA 拷贝到本地 Device Memory 后再访问延迟更低。
Transaction Path 视角的内存访问
从 transaction path 视角看,一个完整的显存访问经历以下阶段:
Virtual address(shader instruction 产生)
→ AGU 计算 per-lane 地址
→ active mask filtering
→ coalescing / request formation
→ TLB lookup(hit:物理地址;miss:page walk)
→ L1 Cache lookup(hit:返回数据)
→ Interconnect fabric arbitration
→ L2 Cache lookup(per-slice)
→ Memory controller queue
→ DRAM command scheduling(FR-FCFS)
→ DRAM transaction
→ Return data path(DRAM → MC → L2 → Fabric → L1 → Result Queue)
→ Dependency clear → Warp eligibility restored
每个环节都可能引入延迟。GPU 通过大量并发 Logical Warp 的交织执行来容忍这些延迟,而不是消除延迟本身。
4.5 Interconnect Fabric
GPU 内部集成了大量计算单元、缓存和专用功能模块。Interconnect Fabric(IF)是连接这些模块、提供数据交换通路的通信网络。
拓扑形式
Bus。所有模块共享一条公共 Bus。早期小规模 GPU 采用。Bus 的可扩展性差,模块数量上升时成为单点瓶颈。现代中高端 GPU 内部不再采用纯共享 Bus 作为主要互连。
Ring。模块按环状连接,数据沿环单方向或双方向流动。增加模块仅需加入环路。AMD 曾在早期 GPU 和 APU 中采用 Ring。Ring 的延迟与模块数线性相关,通信量集中在某一段时可能发生拥塞。
Crossbar。全连接网络,每个输入端口到每个输出端口有独立通路,单跳直达。硬件代价随节点数呈平方级增长,通常仅用于模块数少的场合或作为复杂网络的局部组件。
Mesh / NoC。当前主流大规模 GPU 采用的架构。Macro-Core、缓存 slice、内存控制器被视作网络节点,芯片上以路由器和链路组成网络。数据包通过多跳传输到达任意节点。可扩展性好,节点数增加时硬件开销线性增长,能并行处理多数据包。NVIDIA Ampere/Ada 和 AMD RDNA/CDNA 采用基于 NoC 的设计。挑战在于路由策略和流控机制的设计,典型做法包括 Wormhole 路由、虚拟信道避免阻塞。
混合拓扑。SM 分组,组内 Crossbar,组间 Ring。或树状结构分层汇聚带宽。实际硬件中常为复合方案。
与一致性协议的关系
Interconnect Fabric 承载的不只是数据传输,还包括缓存一致性协议的协调消息。GPU 采用弱一致性模型以减少互连流量,但即使是弱一致性也需要 fabric 传递 barrier、flush、invalidation 等控制消息。
支持全局 L1 缓存一致性要求 fabric 承担额外的 probe 和 invalidation 消息,占用带宽并增加延迟。大多数 GPU 选择弱化 L1 一致性,由 L2 作为全局一致点,Macro-Core 间的数据同步通过 L2 和显式 barrier 实现。
多芯片互连
硬件调度多芯片 GPU(MCM GPU)中,芯片间互连带宽和延迟是决定可伸缩性的关键因素。高速 Chiplet 互连技术将多个 Die 整合为逻辑单一 GPU,互连技术从片上拓展到片间,fabric 的设计复杂度进一步上升。
4.6 Schedule
GPU 需要同时服务于图形渲染、通用计算、视频编解码、数据搬运等多种任务。调度系统的核心目标是确保任务依赖顺序的正确性,同时最大化 GPU 各单元的利用率。
引擎竞争与调度
GPU 内部包含多种专用引擎:Graphics Engine、Compute Engine、Copy Engine、Video Codec Engine。这些引擎可以并行工作,但当它们需要访问共享资源(显存、fabric 带宽)时产生竞争。驱动和硬件调度器负责在不同任务间进行仲裁。
Media / Copy / DMA Path 是 Specialized Units 的第四条路径。Copy Engine、DMA Engine、Video Codec Engine 在现代 GPU 中占据相当的芯片面积和功耗预算。它们与 graphics/compute 路径共享 memory bandwidth,存在带宽竞争关系。当 Copy Engine 执行大规模数据搬运或视频解码时,会占用 memory controller 与 fabric 的带宽配额,影响 shader core 的 memory latency 与 throughput。该路径不应被忽略。
抢占粒度与代价
抢占(Preemption)是中断当前任务以让更高优先级任务运行的机制:
- Draw Call 级抢占:在两次绘制命令之间抢占,最粗糙,延迟最高。
- 三角形级抢占:在一个 Draw Call 内部完成当前三角形后抢占,需要硬件支持保存和恢复更多管线状态。
- 指令级抢占:任何指令执行完毕后抢占,最精细,延迟最低,但硬件实现最复杂。
抢占是系统 UI 流畅性和处理紧急计算任务的必要条件。抢占粒度越细,上下文保存/恢复的开销越大。
Firmware (Fw)
GPU Firmware 是运行在 GPU 内部的微代码,负责底层控制和管理。
引导和初始化。GPU 上电或复位时,Firmware 执行硬件自检(POST)和各单元初始化,设置寄存器默认值,加载电源管理策略,管理安全启动(Secure Boot)。
上下文管理。Firmware 维护任务上下文信息,协助在不同任务或进程间切换上下文:保存和恢复图形/计算管线状态、切换 Register File 和 Page Table Pointer。在支持图形与计算并发的 GPU 中,Firmware 根据任务优先级和超时限制决定是否抢占长时间运行的 Compute Kernel。
硬件调度辅助。Firmware 介入某些调度决策,如 AMD 的 Hardware Scheduler(HWS)固件负责将多个 Command Queue 调度到 ACE 或图形 Command Processor。复杂决策(多队列竞争计算单元)由 Firmware 根据预定策略仲裁。Firmware 升级可改进调度策略,无需硬件改版。
监控与异常处理。Firmware 持续监控温度、电压和各单元负载。检测到死锁或超时时触发 TDR(Timeout Detection & Recovery),向驱动报告,驱动中止当前任务队列并重置 GPU。
Command Processor (CP)
CP 是 GPU 调度系统中直接处理指令流的硬件单元,负责读取和解释主机 CPU 通过驱动提交的 Command Buffer。
命令解析。CP 逐条读取 Command Buffer 中的指令,触发相应 GPU 单元动作:设置寄存器、启动 Draw Call、Dispatch 计算 Kernel、发送复制命令给 DMA 引擎。
状态管理。CP 负责按正确顺序应用状态改变,检测并跳过重复的状态值设置。Shader 程序、Texture Sampler 状态、混合模式、Viewport 大小等大量可配置状态由 CP 管理。
工作分发。CP 将 Draw Call 的图元批次分配给不同 Macro-Core,将 Dispatch 的 Thread Block 调度到各 Macro-Core。通过与 Macro-Core 交互,及时向空闲核心派发新任务。
同步与信号。CP 处理 GPU 内部及 GPU 与 CPU 之间的同步信号。遇到等待 Semaphore/Fence 的命令时暂停后续取指,条件满足后继续。命令执行完毕需要通知 CPU 时,CP 写入内存或寄存器触发中断。
多队列调度。高端 GPU 同时维护多个硬件队列(Graphics、Compute、DMA)。CP 按策略在多个命令流之间切换。异步计算场景中,Compute Queue 上的 Dispatch 与 Graphics Queue 上的 Draw 交织执行,CP 将 Compute Work 送往闲置 Compute 资源,同时 Graphics Work 占用所需资源。
Hardware Queue
Hardware Queue 承载不同类型任务流,每个 Queue 对应一种 Engine 或一组资源。
Graphics Queue。功能最完善,支持 Draw、Clear、Dispatch、内存拷贝。由 Command Processor 和固定功能单元实现,命令执行顺序严格。
Compute Queue。专门用于执行计算着色工作,支持 Dispatch 和内存传输。由独立的 Compute Command Processor 实现,多个 Compute Queue 之间以及与 Graphics Queue 之间可实现硬件级异步计算并发。
DMA/Transfer Queue。专用于内存复制和数据搬运,由独立的 DMA Engine 驱动。与 Compute/Graphics 队列可并行执行。
其他专用队列。Video Queue 用于视频编解码,I/O Queue(DirectStorage 等)处理与 NVMe 等存储器件的数据流。
实现。每个 Hardware Queue 在 GPU 侧通常实现为环形缓冲区(Ring Buffer)。CPU 驱动写入命令并更新尾指针,GPU 硬件读取命令并移动首指针执行。
Observable Stall:Schedule
| Stall 类型 | 可观察症状 |
|---|---|
| 抢占延迟 | Preemption 完成时间过长,UI 卡顿 |
| 队列竞争 | Queue submission 延迟、命令 buffer 堆积 |
| 上下文切换 | Context switch 开销导致 throughput 下降 |
| TDR | GPU 无响应触发重置,任务丢失 |
| Copy/Video 竞争 | Shader memory latency 上升、bandwidth 抖动 |
4.7 Geometry
Geometry System 涵盖 GPU 图形管线中从顶点数据输入到图元准备进行光栅化之间的所有处理环节。现代 GPU 将多数几何处理工作分配给可编程 Shader(Vertex Shader、Tessellation Shader),但仍保留关键固定功能单元以提升效率。分析 Geometry System 需要追踪数据从显存到 rasterizer 的硬件路径,而非停留在功能模块清单。
Geometry Processor (GP)
GP 是 GPU 几何阶段的前端控制器和分发器,负责将 Host 提供的 Draw Command 转化为 GPU 内部的并行几何工作。
Primitive Distributor (PD)。将 Draw Command 指定的 Primitive 均衡分发给 GPU 内部多个并行处理单元。负载均衡策略直接影响几何阶段的吞吐上限。
Vertex Fetch。从显存读取顶点属性数据(坐标、法线、纹理坐标、颜色等)。VAF(Vertex Attribute Fetcher)根据 Vertex Buffer Address 和 Vertex Index 将属性数据读到内部 Register 或 Cache,支持不同 Vertex Format 并执行格式转换。VAF 的效率直接影响 Vertex Processing Throughput。对连续范围的 Vertex,VAF 进行 Prefetching 和 Batch Loading。Index Buffer 复用 Vertex 时,Cache 存储最近访问的 Vertex Data 可减少显存读取。
Coordinate Transformation。Vertex Shader 执行模型变换、视图变换和投影变换,将顶点从局部空间转换到裁剪空间。这些变换通常由可编程 Vertex Shader 完成,但固定功能单元提供辅助。
Tessellation。Tessellator 与 Hull Shader、Domain Shader 配合,将粗糙 Control Mesh 细分为精细 Triangle Mesh。硬件 Tessellator 根据 Hull Shader 计算的 Tessellation Factor 生成新顶点和小三角形。专用电路实现比通用 ALU 吞吐量更高。
Primitive Assembly。将处理后的顶点组装成图元(点、线、三角形)。此阶段执行背面剔除(Back-face Culling),根据图元朝向剔除不可见背面。
Clipping。裁剪位于视锥(View Frustum)之外的图元。完全在视锥内的保留,部分在视锥内的裁剪生成新顶点,完全在外的剔除。
Primitive Unit
Primitive Unit 负责具体 Primitive 级处理的固定功能模块群。
Primitive Culling (PC)。在 Primitive 送往 Rasterization 之前执行多种剔除:View Frustum Culling(丢弃视野外 Primitive)、Back-Face Culling(丢弃背面 Primitive)、Small Triangle / Degenerate Triangle Culling。PC 有效减少进入 Rasterizer 的 Primitive 数量。
Tessellator。固定功能单元,根据 Tessellation Factor 生成细分后的三角形顶点和边。典型实现每 Clock Cycle 可生成多个细分三角形 Vertex,以 Stream 方式输出。
Geometry Path 数据流
Draw Command(CPU 提交)
→ Command Processor 解析
→ Primitive Distributor 分发
→ Vertex Attribute Fetcher 读取顶点数据(Device Memory → Cache → Vertex Shader)
→ Vertex Shader 执行(坐标变换、属性计算)
→ Tessellation(可选:Hull Shader → Tessellator → Domain Shader)
→ Primitive Assembly + Culling
→ Clipping
→ 输出到 Rasterizer
每个环节的输出作为下一环节的输入,中间结果通常驻留在 register 或 cache 中,不写入显存,直到必须跨阶段持久化时才通过 L2/memory 交换。
Observable Stall:Geometry
| Stall 类型 | 可观察症状 |
|---|---|
| Vertex fetch 瓶颈 | Vertex attribute fetch 延迟高、cache miss |
| Tessellation 压力 | Tessellator throughput 饱和、geometry amplification 过高 |
| Culling 效率低 | 进入 rasterizer 的 primitive 数量远超可见数量 |
| 几何放大 | Post-tessellation primitive count 超过 rasterizer 处理能力 |
4.8 Rasterization & Pixel Backend
Rasterization & Pixel Backend 涵盖从几何图元转换为像素到最终像素输出的所有阶段。这一阶段的分析需要区分三条并行路径:color write path、depth path、MSAA resolve path,并明确六种可见性/优化机制的各自定位。
Rasterizer
Rasterizer 将几何图元(三角形)转换为一组 fragment。每个 fragment 对应屏幕上的一个潜在像素,包含像素位置、深度值和从顶点属性插值而来的颜色、纹理坐标等信息。
Rasterizer 是固定功能单元。它根据三角形顶点坐标计算覆盖的像素,利用 edge function 和 tile-based traversal 算法快速完成离散化。Rasterizer 的 throughput 通常以每时钟周期处理的三角形数或像素数衡量。
Interpolator
Interpolator 对 Vertex Shader 输出的属性进行透视校正插值(Perspective-Correct Interpolation),为每个 fragment 计算平滑变化的属性值。插值结果作为 Pixel Shader 的输入。透视校正插值确保 3D 空间中属性变化在投影后仍然正确。
六种可见性与优化机制的显式区分
以下六种机制属于不同概念层级,不可混为一谈。
HSR(Hidden Surface Removal)。可见性优化的总称,指在 fragment shading 之前或期间剔除被遮挡的 fragment,减少 shading 工作量。HSR 是目标而非具体实现。
Early-Z。可见性优化的一种。在 fragment shader 执行前进行 per-fragment 粒度的 depth test,提前丢弃不通过测试的 fragment。Early-Z 的启用受 shader 约束:若 shader 包含 discard、alpha test、shader depth write、UAV side effect 或 early_fragment_tests 修饰,Early-Z 可能被禁用或降级。
LRZ(Low Resolution Z)。可见性优化的粗粒度版本。使用低分辨率 depth buffer 进行保守的 early rejection,在 per-block / per-tile 粒度上跳过无法通过的 tile 或 block。LRZ 与 Early-Z 的 per-fragment 粒度不同,适用于大面积遮挡的快速剔除。
FPK(Fragment Prologue Kill)。Fragment 级别的提前终止机制。在 fragment shader prologue 阶段根据特定条件提前 kill fragment。与 Early-Z 不同,FPK 可能依赖 shader 内部逻辑而非纯硬件 depth test。
TE(Transaction Elimination)。Tile transaction 优化。当 tile 内容在 render pass 内未发生变化时,跳过该 tile 的 store 操作以减少带宽。TE 是带宽优化,不是可见性优化。
UTC(Unified Tile Compression)。压缩保持机制。对 tile 数据进行统一压缩,减少 tile 与外部 memory 之间的数据传输。UTC 是压缩优化,与可见性优化和 transaction elimination 属于不同概念。
可见性优化(HSR、Early-Z、LRZ、FPK)、tile transaction 优化(TE)、压缩保持(UTC)是三个不同概念层级。 Architecture 章不将它们合并为"tile 优化"一笔带过。
技术纠错:Alpha Blending 与 Early-Z
Alpha blending 不必然导致 Late-Z。现代硬件可通过 per-sample 排序、conservative rasterization、early-z with ordered output 等机制在 blend 之前完成 depth test。实际强制 Late-Z 的是以下 shader 行为:shader 内部可能修改 depth(shader depth write)、可能丢弃 fragment(discard / alpha test)、可能产生不可回退的 side effect(UAV write、atomic)。这些约束决定了 depth test 能否提前到 shader 之前执行。
ROP 不应默认固定绑定 memory controller。桌面 dGPU 中 pixel backend 通常与 L2 / memory partition 邻接,但在 TBDR / SoC GPU 中,pixel backend ownership 可能在 tile-local path 上完成,不经过全局 memory controller。
Pixel Backend / ROP / RB
Pixel Backend(ROP / RB)负责 fragment shading 之后的最终像素合并和输出。分析 Pixel Backend 需要按路径拆解。
Color Write Path:
fragment shader color export
→ pixel backend / RB / ROP
→ blend(若启用:C_out = C_src * F_src + C_dst * F_dst)
→ compression metadata 评估(DCC / delta color compression)
→ 若匹配 fast clear 条件:跳过实际写入,仅更新 metadata
→ 若可压缩:compressed write
→ 若不可压缩:uncompressed write
→ L2 Cache slice
→ memory controller
Depth Path:
rasterizer depth output / early-z result
→ Hi-Z / hierarchical depth test(coarse granularity)
→ per-sample / per-pixel depth test
→ depth/stencil backend
→ HTILE / depth compression metadata
→ compressed or uncompressed write
→ L2 / memory
MSAA Resolve Path:
MSAA 为每个像素存储多个 sample 的颜色和深度值。MSAA resolve 将这些 per-sample 值合并为单个像素值。Resolve 可在 pixel backend 中硬件完成(fast resolve),也可通过 shader 手动完成。Pixel backend resolve 的带宽效率远高于 shader resolve。
Compression Metadata
Compression metadata 是 pixel backend 的关键配套机制,不是独立功能单元。
- DCC / Delta Color Compression。存储像素块内颜色值的差异而非绝对值。当 tile/block 内颜色变化平缓时可大幅压缩。Metadata 记录每个 tile/block 的压缩状态。
- HTILE。Hierarchical tile 元数据,用于快速判断 tile 内深度值的同质性,支持快速 clear 和 early rejection。
- Fast Clear Metadata。标记 tile 是否处于 cleared 状态,避免重复写入已知值。
- Transaction Elimination (TE)。标记 tile 内容自加载后是否变化,未变化则跳过 store。
这些 metadata 与 color/depth/stencil surface 并行存储,由 pixel backend 在读写时自动维护和查询。Metadata 本身也占用存储和带宽,存在"metadata traffic"这一可观察指标。
PS Render Target Write 与 CS UAV Write 的路径差异
PS render target write 和 CS UAV write 不是"都写显存",走不同路径:
PS render target write:fragment export → pixel backend / RB → blend / depth / stencil / compression metadata → render target layout → L2 / memory。
CS UAV / storage write:shader LD/ST path → cache / atomic / UAV ordering → L2 / memory → possible compression/layout barrier。
路径不同导致 barrier、layout transition、compression state、cache pollution 的代价不同。PS 路径天然与 render target layout 和 compression metadata 耦合。CS 路径需要显式处理 UAV ordering 和 atomic,且可能触发 compression state 不一致的 fallback。
IMR vs TBDR 的 Pixel Backend Ownership
桌面 IMR GPU 的 pixel backend 通常与 L2 / memory partition 邻接,pixel output 经过 backend 直接写入 L2,再经 memory controller 持久化到显存。
移动 GPU / TBDR 架构的 pixel backend ownership 不同。整个 render pass 的 color/depth/stencil 数据在 Tile-local Storage 上处理,render pass 结束才写回显存。Pixel backend 的操作(blend、depth test、MSAA resolve)在片上 SRAM 中原位完成,无需穿透到显存。如果后处理效果需要跨 tile 访问数据,将强制数据写回显存再重新加载,引发性能下降。
Observable Stall:Rasterization & Pixel Backend
| Stall 类型 | 可观察症状 |
|---|---|
| Early-Z 失效 | Shader 含 discard/depth-write/UAV 时 depth test 移至 Late-Z,shading workload 增加 |
| Pixel backend busy | ROP/RB utilization 饱和,export stall |
| Blend pressure | Blend-enabled render target serialization 增加 |
| Compression fallback | Metadata traffic 增加,uncompressed write 比例上升,bandwidth tax 增加 |
| Depth/stencil busy | Depth test throughput 饱和 |
| TBDR tile flush | Cross-tile access 强制 store/reload,DRAM transaction 激增 |
| MSAA resolve | Resolve pass 增加额外 bandwidth |
4.9 Texture Unit
Texture Unit 是 Macro-Core 中专门负责纹理采样和过滤的模块。图形渲染中纹理采样是最频繁的操作之一,GPU 为此设置固定功能的 Texture Unit 以降低 ALU 成本。分析 Texture Unit 需要追踪从 sample 指令到数据返回的完整 descriptor path,而非仅描述 filtering 算法。
Descriptor Path
Texture path 的完整数据流:
shader sample instruction(携带 texture descriptor index / handle、纹理坐标、LOD bias)
→ descriptor / resource header lookup(从 descriptor cache 或 bindless table)
→ sampler state fetch(filter mode、wrap mode、border color、max anisotropy)
→ coordinate transform(wrap / clamp / mirror / border)
→ LOD calculation(derivatives-based 或 explicit LOD)
→ mipmap level selection + anisotropy footprint calculation
→ texture address generation(texel 物理地址)
→ Texture Cache lookup
→ miss:L2 / memory fetch
→ block compression decode(BCn / ASTC / ETC)
→ texel data return
→ filter 计算(nearest / bilinear / trilinear / anisotropic)
→ optional depth comparison(PCF shadow map)
→ return filtered result to shader
Descriptor lookup 与 bindless 代价。传统模型中,descriptor 在 draw/dispatch 前绑定到固定槽位,descriptor 数据在绑定阶段已驻留 cache。Bindless 模型通过 texture handle 动态 lookup descriptor,减少了 CPU binding overhead,但 GPU 侧需要在每次 sample 时访问 descriptor cache、fetch resource header、解析 sampler state。Bindless 的 working set 超出 descriptor cache 容量时,descriptor cache miss 频率上升,TLB miss 和 page walk 风险增加。
Texture Cache
Texture Cache 是针对 2D 访问局部性优化的专用 L1 Cache。相邻像素通常采样相邻纹理坐标,Texture Cache 按小块(block)缓存 texel 数据以实现高命中率。Texture Cache 通常与 Texture Unit 紧耦合,带宽充足。
纹理数据在显存中通常以压缩 block(4×4 或 8×8 texel group)存储。Cache miss 时取回整个 block,解压后存入 cache。
Coordinate Transformation and Wrap Mode
Texture Unit 硬件处理纹理坐标变换(wrap、mirror、clamp、border 等),以及 texel 地址计算。给定 (u,v) 和 mipmap 层,Texture Unit 计算对应 mipmap 的基址加上 u,v 偏移乘以像素格式大小,得出物理地址。这些整数运算在硬件中快速完成。
Filtering
Texture Unit 内置多种过滤算法的电路:
- Bilinear:对邻近 2×2 共 4 个 texel 加权平均。
- Trilinear:在 bilinear 基础上在两个 mipmap 层间线性插值。
- Anisotropic:沿纹理映射拉伸方向采样多个 texel,等级(2×、4×、8×、16×)通过多个并发采样实现。
过滤计算由硬件完成,对 Shader 透明延迟。若将这些计算置于 ALU 中,将耗费大量指令。
Texture Format and Decoding
Texture Unit 支持多种格式处理:SRGB 采样时 Gamma 校正、depth comparison 纹理(PCF shadow map)、block compressed 纹理(BCn/ASTC/ETC)的硬件 decode。这些操作与具体格式绑定,硬件快速完成。
当 Shader 执行纹理采样指令时,对应 Logical Warp 暂停,Texture Unit 处理并等待结果返回。为隐藏采样延迟,GPU 允许该 Logical Warp 等待时执行其他 Logical Warp 的指令。
Observable Stall:Texture
| Stall 类型 | 可观察症状 |
|---|---|
| Texture Cache miss | Texture Cache hit rate 下降,texture pipe busy 但 ALU idle |
| Sampler bottleneck | 高各向异性等级下 sampler throughput 饱和 |
| descriptor cache miss(bindless) | Descriptor lookup 延迟增加,TLB/page walk 上升 |
| LOD/Anisotropy 开销 | 高 trilinear/anisotropic 设置下 texel fetch 数量增加 |
| Block compression decode | Decompression 增加 latency,但通常被 cache miss 掩盖 |
4.10 Ray Tracing Unit (RTU)
RT acceleration 将 BVH traversal 与 ray-box / ray-triangle intersection 中的固定模式计算移出通用 shader datapath。它降低 intersection 测试的 ALU 成本,但不消除 traversal length、memory locality、shader payload、any-hit 调用、material divergence 对稳态吞吐的决定性影响。
RTU 不是"能在固定时间内完成大量相交判断"的魔法单元。Traversal 长度随 BVH depth、ray distribution、ray type 变化。any-hit shader 调用引入分支发散。payload register pressure 限制 occupancy。BVH node fetch 的随机访问模式影响 cache hit rate。
Acceleration Structure Traversal
光线追踪通过 BVH(Bounding Volume Hierarchy)加速结构在场景中定位 ray 可能碰到的物体。BVH 是一棵层次包围体树。Traversal 过程沿 ray 路径走过 BVH,测试 ray 与节点包围盒的相交。不相交则跳过整棵子树,相交则深入子节点继续。
RTU 以硬件状态机实现 traversal。它将 ray 起点、方向和当前 BVH 节点包围盒作为输入,快速算出交点或判定相交与否,然后决定下一步走向。为支持高吞吐,RTU 使用堆栈硬件记录回溯路径,并配合专用 BVH cache 加速 node data 读取。Hardware 以流水线形式批量处理多个 ray 的 box test。
Traversal 的 memory access pattern 高度随机:BVH node 在内存中的分布通常不遵循访问顺序,node fetch 容易 miss cache。这种随机性是 RT workload 的核心瓶颈之一,不被 RTU 硬化所消除。
Triangle and Surface Intersection
BVH traversal 到达叶节点时,需测试 ray 与其中三角形是否相交及交点距离。RTU 硬件包含快速求解 ray-triangle intersection 的电路(如 Möller-Trumbore 算法的硬件实现),涉及 ray 方向与三角形边缘向量的叉乘和点乘。
RTU 可一次对一束 ray 测试多个三角形,通过内部向量化降低单交点成本。对于曲面或自定义求交体,通常需退化为三角形 mesh 或依赖 software intersection shader。
Hybrid Shader Execution
光线追踪在 API 级实现中是一系列 shader(Ray Generation、Closest Hit、Any Hit、Miss、Intersection 等)与硬件 acceleration 的交互。
RTU 的工作在 shader 调用 trace ray 函数时启动。Hardware 挂起着色器执行,切换到 RTU 状态机遍历 BVH。找到交点后返回信息给 shader。若途中触发 Any Hit Shader,hardware 暂停 traversal 执行该 shader,完成后再继续。Closest Hit / Miss shader 的调度形成新的 dependency chain。
RTU 作为 Macro-Core 的协处理器,能够访问 shader 的寄存器并与其同步。Traversal 期间 ray state 占用寄存器,形成 payload register pressure。Payload size 越大,Macro-Core 能同时驻留的 ray 数量越少,occupancy 越低。
RT Workload 的瓶颈表现
RT workload 的性能问题通常表现为:
- RT pipe pressure:traversal unit 饱和,新 ray 无法及时投入处理。
- Shader core under-utilization:RTU 忙但 shader ALU 空闲,或因等待 traversal 结果导致 eligible warp 不足。
- L1/L2 miss 上升:BVH node fetch 的随机访问模式破坏 cache locality。
- Payload register pressure 上升:ray state 占用过多 register,限制 resident warp 数量。
- Material divergence:any-hit / closest-hit shader 内分支发散严重,active lane 利用率低。
Matrix / Tensor Acceleration Path
同理,Matrix / Tensor acceleration 应写成完整数据流路径而非 Feature 名词:
operand layout(matrix 在 memory/register 中的排布)
→ tile shape 匹配(hardware 支持的 tile 尺寸)
→ operand movement(shared memory / register → tensor datapath)
→ accumulate 运算
→ precision / scale metadata 处理
→ writeback to register / shared memory
专用单元的价值在于将高频、固定模式、可硬化的计算从 general-purpose shader 中拆出。代价是数据准备、同步、精度格式转换、operand movement、pipeline coupling 与调度复杂性。不同厂商的 Matrix / Tensor 单元在 dispatch 方式、管线独立性、与常规执行资源的冲突关系上差异很大。
Observable Stall:RT & Matrix
| Stall 类型 | 可观察症状 |
|---|---|
| Traversal bottleneck | RT pipe busy,traversal depth 变化大 |
| Memory divergence | BVH node fetch L1/L2 miss 高,memory latency 长尾 |
| Payload pressure | Occupancy 下降,register spill 增加 |
| Material divergence | Any-hit shader branch efficiency 低,wavefront occupancy 低 |
| Matrix unit stall | Tensor pipe busy,operand layout mismatch 导致重组开销 |
| Matrix sync | Accumulator synchronization stall,precision conversion 延迟 |
4.11 Summary
从宏观架构视角拆解了现代 GPU 的硬件组成。分析 GPU 架构的可靠方法是追踪三条主路径,而非罗列 Feature 名称。
Execution Path
Front-end dispatch(CP → Command Buffer 解析)
→ Work distribution(Draw/Dispatch → Macro-Core 分配)
→ Macro-Core → Scheduler → Sub-Core / SIMD Partition
→ Logical Warp issue → Vector Lane execution
→ Writeback → Dependency clear
Execution Path 的核心约束是:active Logical Warp 不等于 eligible Logical Warp,eligible 不等于 issued,issued 不等于 dispatched,dispatched 不等于 pipe accepted。Dependency Tracking 的具体实现(NVIDIA scoreboard、AMD waitcnt、Apple opaque tracking)是这条路径上的 vendor-specific 实例。
Memory Path
Lane address generation
→ active mask filtering
→ coalescing / request formation
→ L0/L1/Texture/Constant/Local Shared cache
→ TLB / page walk
→ L2 / LLC / SLC
→ fabric arbitration
→ memory controller
→ DRAM transaction
→ return queue / load data buffer
→ dependency clear
→ issue eligibility restored
Memory Path 的核心约束是:不同 state space(Global/Texture/Constant/Local Shared/Tile-local/Pixel Backend/RT/Matrix)走不同物理路径,每条路径的延迟特征、合并方式、依赖解除机制和 stall 场景各不相同。Texture path 拥有 descriptor/sampler/TLB/block-compression 等专用阶段,与 Global LD/ST path 不可混为一谈。
Graphics Backend Path
Primitive / Raster
→ Early-Z / Hi-Z / LRZ / HSR / FPK(可见性优化层)
→ Pixel Shader(可编程阶段)
→ Pixel Backend(blend / depth / stencil / compression metadata)
→ Tile-local ownership 或 Global framebuffer ownership
→ Resolve / Store
Graphics Backend Path 的核心约束是:PS render target write 与 CS UAV write 走不同路径。可见性优化(HSR/Early-Z/LRZ/FPK)、tile transaction 优化(TE)、压缩保持(UTC)属于三个不同概念层级。桌面 IMR 与移动 TBDR 的 pixel backend ownership 不同。compression metadata(DCC/HTILE/fast clear/TE)是 backend path 的不可分割的组成部分。
三条路径的交汇点
三条路径在 GPU 芯片内通过 Interconnect Fabric 交汇,共享 memory bandwidth 和 L2 Cache。Schedule 系统(Firmware、Command Processor、Hardware Queue)负责协调三条路径上的任务分配和资源共享。Media / Copy / DMA Path 作为第四条独立路径,与三条主路径竞争 memory controller 和 fabric 带宽。
理解这三条主路径的职责与数据流向,是分析 GPU 的必要条件,但尚不充分。宏观架构回答了"数据在哪里流动、经过哪些模块",但尚未回答"每个模块内部如何组织执行、如何隐藏延迟、如何跟踪依赖"。Execution Path 上,Macro-Core 如何调度数万 invocation。Memory Path 上,coalescing 后的 request 如何在 register、shared storage、cache 之间完成 operand delivery。Graphics Backend Path 上,pixel backend 的 blend 与 compression 如何与 shader export 衔接。这些问题的答案都落在微架构(Microarchitecture)层面。后续厂商篇将对这些中性槽位进行具体实例化:NVIDIA 的 SM/Warp/Scoreboard/Tensor/RT/ROP-L2、AMD 的 CU/WGP/Wavefront/waitcnt/LDS/RB/DCC、Apple 的 TBDR/Tile Memory/UMA/SLC 都是同一条分析链条的不同实现。
五、Microarchitecture
前一节从宏观视角梳理了 GPU 的三条主路径(Execution Path、Memory Path、Graphics Backend Path),明确了数据在模块间的流动方向与交汇方式。但 macro-core 如何调度 invocation、register file 如何交付 operand、memory request 返回后如何解除依赖、barrier 如何建立 happens-before 关系,这些都需要进入模块内部的组织结构才能回答。
Microarchitecture 层面:分析 Macro-Core、Register File、Memory Issue Unit、Barrier Unit 等组件的内部运作机制,说明 GPU 如何在硅片上执行大规模并行 invocation。内容从处理器设计的两条分叉路线(CPU 的 OoO 与 GPU 的 Hardware Multithreading)出发,建立 GPU 微架构的分析框架。
5.1 Design Philosophy
现代处理器设计始于简单的有序流水线及其性能瓶颈。演进路径分为两条:一条发展为高度智能化的 CPU,另一条演变为大规模并行的 GPU。对比这两条路径,可以明确 GPU 选择 Many-Core Parallelism 而非 Single-Core Intelligence 的原因。以下展开 Out-of-Order Execution(OoO)与 Hardware Multithreading 两种延迟对抗策略的差异,为理解 Logical Warp 调度、Masked SIMD Execution、内存访问合并等 GPU 关键技术做铺垫。
5.1.1 Performance Metrics: The Optimization Target
微架构设计的核心目标,在半导体与芯片设计领域通常概括为 PPA(Performance、Power、Area 的综合优化)。三者构成难以调和的三角关系,对其中一者的追求常以牺牲其他两者为代价。对 GPU 而言,Performance 是 PPA 三角中最优先关注的指标。
处理器的计算性能由有效计算指令的执行速率决定。"有效"特指推动算法演进的算术逻辑运算(ALU)指令,例如整数与浮点数的加法、乘法、Fused Multiply-Add(FMA)等。其他指令,如加载(Load)、存储(Store)、移动(Move)、分支(Branch)或跳转(Jump),归根结底是为核心计算指令服务,负责准备操作数、搬运计算结果或控制程序流程。
处理器单位时间内完成的有效计算指令数量,决定了其峰值计算性能。该宏观性能指标可拆解为三个关键微观因素的乘积:
Number of Execution Units(EU)
处理器拥有的 ALU 越多,理论上在同一时刻可处理的计算任务越多。这决定了总产量上限。在 GPU 架构中,这些 EU 对应于 Macro-Core 内部的 Vector Lane 执行通道。通过增加 Macro-Core 数量和每核 SIMD Partition 宽度来提升 EU 数量,代价是芯片面积和功耗的线性或超线性增长。
Speed of Execution Units
单个计算单元的工作速度由 Clock Frequency 和 Pipeline Latency 两个子因素决定。Clock Frequency 定义每秒执行的节拍数。Pipeline Latency 代表单个 EU 完成一次完整操作所需的节拍数。提高频率或减少单次操作时间均可提升 EU 速度。GPU 的频率通常低于 CPU(受功耗与散热约束),因此更依赖并行宽度来弥补单通道速度差距。
Utilization of Execution Units
这是决定实际性能的关键因素。即使拥有大量高速 EU,如果因等待数据、等待前序指令完成或等待分支方向确定等原因导致 EU 处于闲置状态(Stall),整体性能下降。Utilization 反映了计算资源在时间维度上被有效利用的比例,衡量理论峰值与实际性能之间的差距。低利用率意味着理论计算能力的浪费。
处理器设计与演进中的所有微架构优化技术,最终都指向上述三个因素中一个或多个的提升。GPU 微架构的全部演进,目标是最大化 EU Utilization。它通过大规模 Hardware Parallelism 和专用调度机制,确保计算单元持续工作,让每个时钟周期内有尽可能多的有用工作正在进行。
为理解 GPU 的技术路径,需要回顾最原始、最简单的计算模型。该简单模型所遭遇的问题(Pipeline Hazards)驱动了后续所有架构创新,也是 GPU 复杂机制存在的根本原因。
5.1.2 Pipeline Hazards
基础处理器核心采用 In-Order Execution 模型:指令严格按程序顺序送入流水线。经典五级流水线(IF → ID → EX → MEM → WB)通过阶段重叠提升吞吐,但三类 Pipeline Hazards 会持续打断流动:
Structural Hazard:多条指令在同一周期争用同一硬件资源(如 IF 取指与 MEM 访存争用统一内存端口)。解决方案是资源复制(分离 I-Cache 与 D-Cache)或插入 Bubble。
Data Hazard:指令间的数据依赖导致后序指令在前序结果就绪前执行。RAW(True Dependence)通过 Forwarding / Bypass 或动态调度解决;WAR 与 WAW(False Dependence)通过 Register Renaming 消除。
Control Hazard:分支指令改变 PC,流水线在分支结果确定前无法决定下一条取指地址。解决方式从停顿等待到 Branch Prediction + Speculative Execution。
三类 Hazards 共同迫使处理器在停顿(插入 Bubble,浪费周期)与增加硬件复杂度(预测、乱序、重命名)之间做取舍。这一取舍是 CPU 与 GPU 走向不同演化道路的直接动因。
5.1.3 CPU: Instruction Level Parallelism (ILP)
CPU 选择集中资源挖掘单线程内部的 ILP,将单线程执行速度推向极限。其设计假设是典型软件负载控制流复杂、内存访问不规则、关键路径高度串行。
为在充满依赖的单一指令流中榨取并行性,CPU 发展出 Out-of-Order Execution(OoO):指令译码后进入 Instruction Window,调度器动态检视窗口内指令的就绪状态,绕过因长延迟操作(如访存)而停顿的指令,将独立指令提前 Issue 到 EU。Register Renaming 是 OoO 的基石:PRF 远大于架构寄存器数量,通过 RAT 映射将 WAW/WAR 转化为无冲突操作。Tomasulo 算法以 Reservation Station 实现分布式调度,RS 监听 CDB 捕获结果,操作数就绪后送入 EU。Branch Prediction + Speculative Execution + ROB 保证乱序执行的指令按程序顺序退休,维护精确异常语义。
CPU OoO 的代价是控制逻辑占用的晶体管与功耗预算极高。现代高性能 CPU 核心的 ROB 可达数百项,PRF 数百个物理寄存器,RS 数十项,Branch Predictor 消耗大量面积与功耗预算。这些开销对 GPU 的并行规模而言不可承受。
5.1.4 GPU: Thread Level Parallelism (TLP)
GPU 的首要任务是处理海量 Data Parallelism:图形渲染中的像素着色、科学计算中的矩阵运算。这类负载的特征明确:
- TLP 极高:可轻易衍生出数百万个独立执行 invocation
- 单个 invocation 的 ILP 相对较低:kernel 相对简短,数据依赖链不长
- 主要瓶颈是访存延迟:每个 invocation 需从内存读写数据,DRAM 访问可达数百至数千时钟周期
若用 CPU 的 OoO 引擎隐藏数百周期的内存延迟,Instruction Window 和 ROB 需容纳数百条指令,硬件开销在 GPU 的并行规模下完全不可行。
GPU 选择通过并发执行海量 invocation 来容忍延迟。核心机制是 Hardware Multithreading:GPU 在硬件层面同时维持大量活跃 invocation 的完整上下文(PC、寄存器),调度器在不同 Logical Warp 间切换时无需操作系统式的上下文保存与恢复。所有活跃 Logical Warp 的状态常驻于片上高速寄存器中,切换开销接近零。
当某个 Logical Warp 因等待长延迟操作(如访存)而停顿时,调度器不在该 Warp 内部寻找独立指令,而是立即切换到另一个已就绪的 Logical Warp。只要有足够多的 Logical Warp 轮流执行,计算单元几乎每个周期都在为某个 Warp 工作,延迟被"淹没"。
这一选择导致 GPU 在 PPA 权衡上与 CPU 相反:
- 将晶体管与功耗预算从复杂的 OoO、Branch Prediction 等控制逻辑中释放,用于堆砌更多结构相对简单的 EU 和支撑海量线程的大容量 Register File
- 流水线通常为 In-Order,不具备 CPU 的通用乱序调度能力
- 依赖检查由轻量级机制完成:compiler 负责大量静态调度,硬件通过 wait barrier、dependency tracking、operand reuse、replay 做有限动态校正
关键判断:GPU 的主路径不是 CPU-style OoO。Warp 内基本仍以 Program Order 为主,Warp 间天然交织执行。这种"看起来像乱序"的现象实际上是 TLP,不是 OoO。
5.1.5 Fixed-Latency vs Variable-Latency Dependency
理解 GPU 的依赖管理需要区分两类延迟:
Fixed-Latency Dependency
ALU-ALU 短延迟、SFU 固定流水线延迟等。延迟在执行前已知,通常由 compiler static scheduling 处理:compiler 在已知延迟的指令间插入无关指令填充等待周期,或通过 forwarding / bypass 路径消除部分延迟。这类依赖不需要运行时动态等待机制。
"短"字的量级有横向实测可查:Chips and Cheese 对 Ada Lovelace 与 RDNA 2 / RDNA 3 的微基准测试显示,常见 FP32 加法与 FMA 的 issue-to-use 延迟在 NVIDIA 架构上为 4 个时钟周期,在 AMD RDNA 系列上为 5 个时钟周期。延迟固定且只有个位数周期,这正是 compiler 能够用静态调度把等待窗口填掉的物理前提。
Variable-Latency Dependency
Memory return、cache miss、TLB miss、texture fetch、Local Shared Storage bank conflict 等。延迟在执行前不可精确预测,需要运行时动态等待机制。Variable-Latency Dependency 是 GPU dependency tracking 机制存在的主要动因。不同厂商的处理方式不同:NVIDIA 通过 hardware-managed scoreboard 在运行时判断 warp eligibility。AMD 通过 compiler 插入的 waitcnt 配合 hardware dependency counter 判断 wavefront 就绪状态。Apple / mobile GPU 通过不公开内部细节的硬件机制处理 memory return、barrier、tile-local drain 与 pipe backpressure。
这一区分是理解 GPU 不同 dependency tracking 实现路径差异的工程基础。
5.2 Work Organization and Execution Granularity
GPU 执行海量 invocation,需要在硬件层面将其组织为层次化的执行群体。据此建立四层结构,作为跨厂商分析的契约。
Invocation / Thread
Shader 或 Kernel 中的逻辑执行实例。开发者编写 kernel 时,视角是单个 invocation 的视角:描述一个 invocation 如何加载数据、执行计算、存储结果。Invocation 是编程模型的逻辑单位,不等于硬件上的 Vector Lane。
Vector Lane
SIMD datapath 中的物理或逻辑执行通道。一条 SIMD 指令在同一周期驱动多个 Vector Lane,每个 Lane 处理一个 invocation 的私有数据。Vector Lane 是物理执行资源的单位。
CUDA thread(NVIDIA 的编程模型概念)不等于 hardware Vector Lane。编程模型中的 thread 是逻辑实体,Vector Lane 是物理 datapath 通道。Warp lane 也不等于 CUDA Core。这些区分对后续分析占用率(occupancy)和寄存器压力有直接影响。
Logical Warp
Warp / Wavefront / SIMD-group / subgroup / execution group。它是调度与 masked execution 的基本粒度。全系列统一用 Logical Warp 作为跨厂商中性术语:
- NVIDIA 对应 Warp(32 线程)
- AMD 对应 Wavefront(通常为 64 线程)
- Apple / Intel / mobile GPU 对应 SIMD-group / subgroup / execution group(宽度因架构而异)
宽度的取值有一手出处可查:NVIDIA 在 CUDA 官方编程指南的 Hardware Implementation / SIMT 章节中写明,multiprocessor 以 32 个线程为一组创建、管理、调度并执行 warp。32 这个宽度写在硬件实现章节里,不是编程模型层的约定。
Logical Warp 内的 invocation 共享同一指令流,在同一时钟周期执行同一条指令,但操作不同的数据。它们共享同一个 PC(或统一的控制流状态),由 Logical-Warp Scheduler / Wave Scheduler / Execution-Group Scheduler 统一调度。每个 invocation 在 Logical Warp 内有唯一 ID,用于访问私有数据或执行条件分支。
Logical Warp 是 GPU 隐藏延迟的基本单位。Macro-Core(SM / CU / WGP / Shader Core)内同时维持多个 Logical Warp 的硬件上下文,调度器在不同 Logical Warp 间快速切换以"淹没"访存延迟。
Workgroup
CTA / Thread Block / Threadgroup / Workgroup。它是 Local Shared Storage(shared memory / LDS / SLM)、barrier、synchronization、resource allocation 的更大粒度。一个 Workgroup 包含多个 Logical Warp,同一 Workgroup 内的 Logical Warp 之间可以通过 Local Shared Storage 共享数据,并通过 barrier 同步执行进度。
Workgroup 不等于 Logical Warp。一个 Workgroup 通常包含数十到数百个 invocation,被划分为若干个 Logical Warp。Workgroup 是资源分配与同步的粒度,Logical Warp 是调度与执行的粒度。两层之间的映射关系影响 occupancy 计算和 shared memory 分配策略。
四层结构的层次关系:
Workgroup(资源分配 / 同步粒度)
└── Logical Warp × N(调度 / masked execution 粒度)
└── Vector Lane × Width(物理 datapath 通道)
└── Invocation(逻辑执行实例)
5.3 Masked SIMD Execution
5.3.1 SPMD / SIMT-like Programming Model vs SIMD Datapath
现代 GPU 通常向 shader language 暴露 SPMD / SIMT-like 编程模型。开发者编写标量 kernel,视角是单个 invocation 的顺序执行逻辑。硬件内部通常用 SIMD datapath 执行多个 invocation。调度单位可表现为 Warp、Wavefront、SIMD-group、subgroup 或 vendor-specific execution group。
这一抽象层的作用是:开发者不需要手动将数据打包成向量格式,不需要显式操作向量指令。Compiler 与硬件将独立的标量 invocation 在运行时自动聚合到 SIMD datapath 上执行。
对比 SIMD 与 SPMD / SIMT-like 模型:
| 维度 | SIMD | SPMD / SIMT-like |
|---|---|---|
| 执行实体 | 数据向量中的元素 | 逻辑上独立的 invocation |
| 编程抽象 | 显式向量指令与数据结构 | 标量代码,并行性由硬件自动管理 |
| 控制流 | 需手动 masking 处理条件分支 | 硬件通过 active mask / predicate 自动处理 divergence |
| 数据打包 | 程序员手动打包/解包 | 由 compiler 与硬件自动完成 |
SIMD 模型存在利用率问题:处理的数据量若非向量宽度的整数倍,最后一个向量中部分 Vector Lane 被闲置。SPMD / SIMT-like 模型通过动态 invocation grouping 缓解这一问题,但引入了新的控制流挑战:divergence。
5.3.2 Branch Divergence and Reconvergence
当 Logical Warp 内的 invocation 遇到条件分支且选择不同执行路径时,发生 Branch Divergence。由于 Logical Warp 内的所有 Vector Lane 在同一周期执行同一条指令,硬件无法让部分 Lane 走 if 路径、另一部分走 else 路径。
硬件解决方案是 masking + 串行化执行:
- 遇到分支时,硬件记录当前执行上下文(PC、active mask),保存到 Divergence / Mask / Reconvergence State(原 Stack Unit)
- 先执行 if 路径,active mask 仅使选择 if 的 Lane 生效,其他 Lane 被禁用
- if 路径完成后,切换到 else 路径,更新 active mask
- 所有路径执行完毕后,通过 Reconvergence 机制恢复统一执行
Reconvergence 通常基于 Post-Dominator 算法:compiler 在代码中标记 reconvergence point(通常是 if-else 结构的 immediate post-dominator),硬件在该点等待所有分支路径到达后统一恢复。不同厂商的实现有差异:NVIDIA 使用 branch stack 管理 divergence 层级。AMD 使用 EXEC mask 配合控制流指令。mobile GPU 同样存在 reconvergence 机制但通常不对外公开细节。
Branch Divergence 的代价是串行化:原本并行的 Vector Lane 因分支被分时复用,有效并行度下降。GPU 编程实践中鼓励编写分支统一的代码,或在 warp/wavefront 级别保证分支一致性。
5.3.3 Limited ILP within Logical Warp
GPU 核心是 TLP,但部分现代架构引入了有限的 Superscalar 特性:同一周期内对同一个 Logical Warp 发射多于一条指令。例如同时发射一条算术指令(FMA)和一条访存指令(LOAD),因为它们使用不同的执行管线。这是在 TLP 基础上对单个 Logical Warp 内部 ILP 的补充挖掘,复杂度和应用范围远小于 CPU 的通用乱序 Superscalar 引擎。GPU 的设计优先级始终是 TLP。
5.4 Dependency Tracking
Dependency Tracking 是 GPU 微架构的核心机制之一,负责管理指令从译码到执行完成全生命周期中的依赖关系。先建立跨厂商分析框架,再将具体实现映射为实例。
总框架:指令能够发射并正确执行,需要满足多维就绪条件。任何一维未满足,指令就被阻塞。各维度之间独立演进、独立解除阻塞。
5.4.1 Instruction readiness
指令本身的有效性:已正确译码、属于当前 Logical Warp 的有效指令流、未被分支预测错误或 reconvergence 机制标记为无效。Instruction Cache 的取指路径必须已将指令字送达 decode unit。
5.4.2 Operand readiness
指令所需的源操作数已可用。操作数可能来自:Register File 读端口(Vector RF 或 Scalar / Uniform RF)、Operand Collector 缓存、Forwarding / Bypass 网络(前序指令结果尚未写回 RF 但已可通过旁路获取)、立即数或常量路径。
5.4.3 Register dependency
前序指令对目标寄存器的写入尚未完成,后序指令不能读取该寄存器。这是 GPU dependency tracking 中最常见的阻塞源。Register dependency 的解除时机取决于产生结果的指令类型:ALU 结果通常在固定延迟后可用。Load 结果在 variable-latency memory return 后可用。
5.4.4 Memory dependency
前序 Store 与后序 Load 之间的地址依赖、atomic 操作的排序要求、memory fence 的语义约束。Memory dependency 涉及更复杂的硬件机制:LSU 内部的 request queue ordering、cache coherence 协议、以及不同 memory scope 的可见性保证。
5.4.5 Barrier dependency
Workgroup 内所有 Logical Warp 必须到达 barrier 点后,才能继续执行后续指令。Barrier 依赖的解除需要跨 Logical Warp 的同步状态收集。详见 5.7 节。
5.4.6 Execution pipe availability
指令所需的执行管线(ALU pipe、SFU pipe、LSU pipe、Matrix / Tensor pipe 等)在当前周期有空闲 slot。不同指令类型绑定不同 pipe,同一周期多个就绪指令可能争用同一 pipe,产生 structural hazard。
5.4.7 Structural hazard
硬件资源(RF 读端口、EU slot、issue slot、dispatch port)不足以同时服务所有就绪指令。Structural hazard 不同于数据依赖:阻塞原因在于物理资源不够,数据本身已经就绪。解决方式包括等待下一周期、多 bank 仲裁、partition 级别的资源隔离。
5.4.8 Queue / buffer backpressure
流水线后端的缓冲资源满溢,向前端施加反压。例如 Result Queue 接近容量上限时,会阻止新指令发射以避免结果丢失。Memory request queue 满时,新的 Load/Store 指令无法进入 LSU。Queue backpressure 是跨阶段耦合的关键机制。
5.4.9 Dependency clear / wakeup
依赖解除后,硬件必须唤醒等待中的指令。_WAKEUP 路径的设计直接影响调度效率:
- 写回阶段(Write Back)将结果写入 RF 后,通知 Dependency Tracking 机制相应寄存器标记为"可读"
- 等待该结果的 Logical Warp 恢复 eligible 状态,有机会被 Logical-Warp Scheduler 重新选中
- Load 指令结果从 memory 返回后,通过 return queue / load data buffer 写回 RF,触发相同 wakeup 路径
- Variable-latency 操作的返回可能跨越数百周期, wakeup 路径必须能够处理长延迟的异步通知
5.4.10 Implementation Examples
上述框架的各维度在不同 GPU 上有不同的实现策略。以下为三种代表性路径:
NVIDIA-style hardware-managed scoreboard
Scoreboard 是 hardware-managed dependency tracking 的一种实现,不是 GPU 依赖管理的总称。NVIDIA 架构中,Scoreboard 以 Logical Warp 为粒度跟踪各指令的寄存器依赖状态。每条指令发射时,Scoreboard 记录其目标寄存器的写挂起状态。后续消费该寄存器的指令被标记为不可发射,直到写回完成、Scoreboard 清除挂起标志。Scoreboard 控制 Logical Warp 的 issue eligibility,但不构造通用的 OoO Window。Warp 内指令仍大致按 program order 流动。
NVIDIA SASS(native ISA)将 scheduling control 信息编码在指令中,compiler 利用已知延迟插入静态调度信息,hardware scoreboard 在此基础上做运行时动态校正。短延迟 ALU 依赖通常由 compiler scheduling + forwarding 处理。variable-latency memory 依赖由 scoreboard 动态跟踪。
AMD-style waitcnt / dependency counter
AMD GCN / RDNA 将部分可变延迟依赖显式暴露给 ISA 与 compiler。Compiler 在需要消费 VMEM、LDS、export 等结果前插入 wait 指令(waitcnt),硬件维护 dependency counter 跟踪各类操作的未完成数量。Counter 归零时后续指令才可执行。短延迟 ALU 依赖通常由 compiler scheduling、forwarding / bypass、pipeline interlock 与 issue rule 共同处理。
这套分工在 AMD 官方 ROCm 文档里有硬性表述:ISA 暴露 vmcnt、expcnt、lgkmcnt 三类硬件计数器,分别跟踪 vector memory、export 与 LDS / constant / message 三类在途操作,s_waitcnt 指令等待对应计数器降到指定值才放行后续指令。文档的内存模型章节进一步把 wait 的放置写成 compiler 的责任:任何 global / local load 的结果在被消费之前,必须由编译器插入对应的 s_waitcnt。漏插或错序,消费者读到的就是尚未返回的旧值。在这条路线里,依赖管理是 ISA 契约的一部分,硬件并不将其封装为黑盒。
AMD 架构同时区分 Scalar ALU / Vector ALU 路径,SGPR(scalar register)与 VGPR(vector register)有独立的 dependency tracking 逻辑。Scalar 路径处理 warp-uniform 操作(地址计算、分支条件、常量加载),Vector 路径处理 per-lane 数据操作。这一分离使 scalar dependency 可以在 vector pipe busy 时独立推进。
Apple / mobile opaque hardware tracking
Apple 及多数 mobile GPU 不公开 dependency tracking 的内部机制。Shader compiler 生成 intermediate representation(如 Metal IR),由 driver / firmware 翻译为最终硬件指令。ISA 层面通常不暴露显式的 wait 指令或 scoreboard 控制字段。硬件内部仍必须处理 memory return、barrier、tile-local drain、pipe backpressure 与 structural hazard,但这些机制对 compiler 和开发者不可见。
这一设计差异的含义是依赖管理的责任在不同层次(hardware vs compiler vs driver)之间的分配不同,mobile GPU 同样需要 dependency tracking。
5.5 Register File / Operand Collection
5.5.1 Six-Level Register / Storage Model
GPU 的寄存器与本地存储资源分为六个层级,覆盖从 per-lane 私有值到 spill 后备存储的完整谱系:
1. Vector RF(per-lane private values)
每个 invocation 私有的通用寄存器,存储局部变量与计算结果。在 AMD 架构中对应 VGPR。NVIDIA 架构提供统一的寄存器文件但每个 invocation 访问其私有区域。Vector RF 是 GPU 面积与功耗的主要消费者之一。
2. Scalar / Uniform RF(wave/warp-uniform values)
Logical Warp 内所有 invocation 共享的值:常量、统一变量、地址基址、控制流状态。AMD 架构中对应 SGPR,有独立的 Scalar ALU 路径。Scalar / Uniform RF 不应仅在 AMD 厂商篇出现:warp-uniform 路径的存在与否、如何利用 uniform 数据减少 Vector RF 压力,是跨厂商的分析维度。当 compiler 识别出 warp-uniform 变量并将其提升到 scalar path 时,可减少 VGPR 占用、提高 occupancy。
3. Predicate / Mask Register(EXEC mask / active mask / predicate)
记录 Logical Warp 内各 Vector Lane 的活跃状态。Branch divergence 时,active mask 禁用未选择当前路径的 Lane。reconvergence 时恢复。AMD 对应 EXEC mask。NVIDIA 对应 predicate / active mask。Predicate Register 是 masked SIMD execution 的核心状态。
4. Local Shared Storage(Shared Memory / LDS / SLM / threadgroup memory)
Workgroup 级别的可编程共享存储,同一 Workgroup 内的 Logical Warp 可读写。用于 invocation 间数据交换、reduction、producer-consumer 模式。物理实现为多 bank SRAM,bank conflict 会 serialize 访问。
5. Tile-local Storage(tile color/depth/stencil/imageblock working set)
Render pass 级别的片上驻留存储,存储 tile 的 color、depth、stencil、MSAA 样本数据。Tile-local Storage 与 Local Shared Storage 都属于片上 SRAM 类资源,但是否物理复用、如何分区、是否对 shader 可见、生命周期如何管理,取决于厂商与代际。Apple 的 Tile Memory / Imageblock、PowerVR 的 tile buffer 都是 Tile-local Storage 的实例。
6. Scratch / Local Memory(spill / private memory backed by cache/DRAM)
当 register pressure 过高、register allocation 无法将所有 live range 放入物理 RF 时,compiler 将部分变量 spill 到 Scratch Memory。Scratch Memory 逻辑上属于 private per-invocation 存储,物理上由 L1 cache 或 DRAM 支撑。延迟远高于 RF,访问模式类似 global LD/ST,但地址空间是 private 的。
Scratch Memory 访问增加是可观察的性能退化信号:occupancy 下降、memory traffic 上升、eligible warp/wavefront 减少。Compiler 在 register allocation 阶段面临取舍:用更多寄存器保存中间值(减少 recomputation、减少 memory traffic)vs 减少寄存器使用(避免 spill、维持更高 occupancy)。
5.5.2 Register Count → Occupancy → Latency Hiding Causal Chain
Register File 的大小直接约束 Macro-Core 内可同时驻留的 Logical Warp 数量:
Register count per invocation
→ Resident logical warp / wavefront count per Macro-Core
→ Occupancy(resident warp 占 hardware limit 的比例)
→ Latency hiding capacity(可切换的就绪 warp 池大小)
→ Issue eligibility(scheduler 是否有就绪 warp 可选)
每个 invocation 分配的 register 数量越多,Macro-Core 内可同时驻留的 invocation 数量就越少,occupancy 越低。当发生 variable-latency 操作(如访存)时,低 occupancy 意味着可供切换的就绪 warp 更少,延迟隐藏能力下降,EU 更容易因无就绪 warp 而 idle。
反向约束同样重要:
- 更高 occupancy 不等于更高 utilization。如果所有 warp 同时等待同一资源(如 cache miss 风暴),即使 occupancy 很高,EU 仍可能 idle。
- 过低 register pressure 可能导致 recomputation(频繁重新计算中间值)或增加 memory traffic(频繁从 memory 重读数据)。
- 过高 register pressure 导致 spill / scratch / local memory 访问,将 RF 压力转化为 memory 压力。
- RF bank conflict、read port pressure、operand collector pressure、live range、reuse distance 也会限制 issue rate,即使 occupancy 看似充足。
5.5.3 Why GPU RF must be banked: CPU vs GPU port count constraint
CPU Register File 规模较小(16-32 个架构 GPR + ~192 个 rename register),OoO 引擎通常 4-6 issue 宽度,同时需要的读端口数约 8-12 个。SRAM 面积和布线复杂度在这个端口数量下完全可控,CPU 可以为 RF 提供充足端口,不需要 bank 化。
GPU 的约束完全不同。一个 32-lane SIMD group 的一条指令需要同时为 32 个 lane 各读取 2-3 个操作数,即同一周期需要 64-96 个读端口。再加上多个 Logical Warp 并发驻留,共享同一 RF 的总端口需求可能达到数百。SRAM 面积和功耗随端口数近似二次方增长,全端口 RF 在物理上不可行。
因此 GPU 采用 bank 化是并行规模驱动的必然结果,全端口 RF 在面积和功耗上不可行,只能将 RF 组织为多 bank,每个 bank 提供有限读写端口。当同一周期多个 lane 访问同一 bank 的不同地址时,端口无法同时服务全部请求,产生 bank conflict。
Local Shared Storage 面临同样的约束:32 lane 同时访问,全端口 SRAM 面积不可承受,因此同样采用多 bank 组织。Bank conflict 在 RF 和 Local Shared Storage 上都是结构性问题,根源是并行规模与端口资源之间的物理矛盾。
5.5.4 Operand Collector (OC)
Operand Collector 是位于 Register File 与 Execution Unit 之间的缓冲与仲裁机制。其核心功能是将 RF 带宽压力、bank conflict、端口冲突和短程重用转化为更可控的收集与仲裁问题。
操作数收集:OC 从 RF 中收集指令所需的源操作数。多条指令同时需要操作数时,OC 协调对 RF 的访问请求,避免端口冲突。
冲突仲裁:当多个 EU 同时请求访问 RF 的同一 bank 或同一端口时,OC 通过缓冲和排队机制 serialize 访问,确保 RF 的充分利用。
数据转发与短程重用:OC 包含 Operand Cache 和 Forwarding Network。Producer-Consumer 紧邻的指令对(如 ALU 结果直接作为下一条 ALU 的输入)可以通过 forwarding path 获取操作数,避免写回 RF 再读取的完整周期。Reuse flag(如 NVIDIA 的 reuse bit)标记可被后续指令重用的操作数,减少 RF 读端口压力。
Operand Collector 不消除 bank conflict,而是将冲突的影响从"直接 stall EU"转化为"在 OC 层面排队等待",给 scheduler 更多调度弹性。
5.6 Memory Issue and Variable-Latency Return
5.6.1 LSU and Coalescing
Load Store Unit (LSU) 负责处理 Logical Warp 的内存访问指令,包括全局内存读写、Local Shared Storage 访问、atomic 操作等。LSU 是计算核心与 memory system 的桥梁。
LSU 执行流程:
- Address Generation:Address Generation Unit (AGU) 以 SIMD 方式同时计算 Logical Warp 内所有活跃 Lane 的有效内存地址
- Active Mask Filtering:根据当前 active mask / predicate 过滤掉非活跃 Lane 的地址请求。未过滤的无效地址会形成无效 transaction,浪费 cache line 与带宽
- Coalescing / Request Formation:检测 Lane 地址模式,将连续或落在同一 cache line 的地址合并为少量内存请求。理想情况下,32 个 thread 访问连续数据,LSU 只需发出 1-2 个 cache line 请求
- Cache / Memory Access:合并后的请求发送到 L0/L1 data cache 或更下层级。未命中时由 memory system 取数据
- Variable-Latency Return:等待数据返回,期间 Logical Warp 被标记为"内存等待"
- Result Writeback:返回数据通过 Result Queue / Load Data Buffer 写回 Vector RF,触发 dependency clear
Coalescing 的效率直接影响 memory bandwidth 利用率。Strided 或随机访问模式降低 coalescing 效率,产生更多独立 memory transaction,增加 cache pressure 和 bandwidth consumption。
5.6.2 Variable-Latency Return and Replay / Reissue
Memory 操作的返回时间不固定,这是 GPU 与 CPU 在依赖管理上的核心差异之一。Variable-latency 的来源包括:
- L1 / L0 cache hit:较短延迟(数周期到数十周期)
- L1 miss, L2 hit:中等延迟(数十到数百周期)
- L2 miss, DRAM access:长延迟(数百到数千周期)
- TLB miss, page walk:额外延迟(数百到数千周期)
- Texture / Sampler path:涉及 descriptor lookup、coordinate transform、filtering,延迟更不可预测
- Local Shared Storage bank conflict:serialize 访问,产生额外等待
Replay / Reissue 机制
某些场景下已发射的指令需要重新执行:cache miss 时地址翻译未完成、coalescing 不充分需重新分组、bank conflict 需分阶段访问、TLB miss 需 page walk 完成后重试。Replay 与 Reissue 是不同层级的机制:Replay 通常在 LSU / pipe 层面重试同一条指令。Reissue 可能涉及回到 scheduler 重新排队。
Scoreboard 负责跟踪依赖,但 Replay / Reissue 是另一层机制:dependency tracking 解决"结果何时可用"的问题,replay 解决"指令是否需要重新执行"的问题。二者协作:replay 发生后,原指令的 dependency tracking 状态重置,重新进入等待-就绪-发射流程。
5.6.3 Result Queue / Load Data Buffer
EU 完成指令计算后,结果需写回 Register File。各 EU 可能在不同时钟周期完成,Result Queue 协调结果的有序写回:
- Writeback throttling:RF 写端口有限,EU 可能同周期输出多条结果,Result Queue 接收并排队逐一写回
- Order maintenance:保持同一 Logical Warp 的写回顺序不破坏程序语义
- Multi-result assembly:某些指令产生多个结果(如 double-precision 运算、vector load),Result Queue 暂存部分结果后组装写回
- Wakeup propagation:结果写回 RF 后,通知 Dependency Tracking 机制清除依赖,等待该结果的 Logical Warp 恢复 eligible
从 memory / L2 cache 返回的 Load 数据也可视为 Result Queue 的一部分。LSU 内部通常设计专门的 Load Data Buffer / Return Buffer,存放多个 Logical Warp 的返回数据并标记归属,数据就位后通知 scheduler 对应 Warp 恢复 eligible。
5.7 Barrier / Synchronization / Memory Ordering
5.7.1 Workgroup Barrier
Barrier 确保 Workgroup 内所有 Logical Warp 都到达同一执行点后,才能继续执行后续指令。实现上,每个 Logical Warp 到达 barrier 时递增计数器。当计数器等于 Workgroup 内 Logical Warp 总数时,barrier 解除,所有 Warp 继续执行。
Barrier 的实现需要 hardware 支持:Macro-Core 内维护 barrier 状态寄存器或计数器,到达 barrier 的 Warp 被标记为等待,scheduler 不再选中这些 Warp。Barrier 解除时,所有等待 Warp 同时恢复 eligible。
Barrier 是 Workgroup 级别的同步原语,不保证跨 Workgroup 的同步顺序。若需要全局同步,通常依赖 host-side synchronization(如 command buffer boundary)或 atomic operation on global memory。
5.7.2 Atomic Operations
Atomic 操作保证多 invocation 并发访问同一内存地址时的操作原子性。GPU LSU 通常内建 atomic 操作单元,支持 add、min、max、compare-and-swap 等操作。Atomic 操作的实现方式因内存类型而异:
- Local Shared Storage atomic:在 Macro-Core 内部的 LSU / LDS unit 中直接完成,延迟较低
- Global memory atomic:可能涉及 L2 cache lock、memory controller 的 atomic unit、或 cache coherence 协议中的 atomic 语义,延迟更高且可能产生更多 inter-partition traffic
Atomic 操作的性能特征:高竞争场景下(大量 invocation 同时访问同一地址),serialize 严重,成为瓶颈。低竞争场景下开销与普通 LD/ST 接近。
5.7.3 Memory Ordering
GPU memory model 通常比 CPU 宽松。GPU 的执行模型假设大部分 invocation 之间的数据访问是独立的,因此默认不保证全局的顺序一致性。显式的 memory fence / barrier 指令用于在需要时建立顺序保证:
- Workgroup-scope fence:保证同一 Workgroup 内 memory 操作的可见性顺序
- Device-scope fence:保证所有 Workgroup 间 memory 操作的可见性顺序
- Acquire-Release 语义:用于 producer-consumer 模式,确保 release 操作之前的写对 acquire 操作之后的读可见
Memory ordering 与 cache coherency 的关系:GPU cache hierarchy(详见 Architecture 章)提供 coherence 的硬件基础,但 memory ordering 的语义保证由 ISA / compiler / hardware 共同实现。Fence 指令可能映射为 cache flush / invalidate、memory controller barrier、或更轻量的 ordering marker,具体实现因架构而异。
Memory ordering 约束的代价:更强的 ordering 要求意味着更多的 synchronization point、更大的 stall 窗口、更高的 cache traffic。GPU 编程实践中倾向于最小化 synchronization scope 和 frequency。
5.8 Pipeline Pressure and Observable Stall
5.8.1 Six-Level Inequality Chain
GPU 流水线中,一个 invocation 从"被创建"到"完成有效计算"之间,存在多个递减环节。理解这些环节之间的间隙,是定位性能瓶颈的基础。
Active logical warp
≠ Eligible logical warp(有未解除的依赖或 barrier)
≠ Issued(scheduler 未选中,或 issue slot 被占用)
≠ Dispatched(dispatch unit 未分配资源)
≠ Pipe accepted(执行 pipe 满或 structural hazard)
≠ Dependency cleared(结果未写回,依赖未解除)
≠ No downstream pressure(下游 queue / buffer backpressure)
这条不等式链的含义:即使 Macro-Core 内有大量 active warp,任何一个环节的阻塞都会减少实际进入 EU 的有效工作量。Pipeline pressure 是多环节瓶颈的叠加效果,无法归结为单一瓶颈。
Active logical warp:Macro-Core 内已分配硬件上下文的 warp 总数。受限于 register file 大小、Local Shared Storage 容量、Workgroup 配置。
Eligible logical warp:Active warp 中,所有 dependency 已满足、不在 barrier 等待、不在 sleep 状态的子集。Eligible warp 比例低意味着 invocation 花费大量时间等待依赖解除。常见原因:variable-latency memory return、register dependency chain 长、barrier 等待。
Issued:Eligible warp 中被 scheduler 选中的子集。Scheduler 可能因 issue slot 有限、优先级策略、或同周期多个 eligible warp 争用同一资源而未能选中所有 eligible warp。
Dispatched:已 issue 的指令中被 Dispatch Unit 接受并分配到具体 EU 的子集。Dispatch 受限于 dispatch port 数量和类型匹配。
Pipe accepted:已 dispatch 的指令中被目标执行 pipe 接受的子集。Pipe 可能因内部 buffer 满、前序指令未离开、或多条指令同周期争用而拒绝接受。
Dependency cleared:Pipe 中完成的指令,其结果已写回 RF 且 dependency tracking 已更新。Variable-latency 操作的 dependency clear 可能发生在执行完成后数百周期。
No downstream pressure:Dependency cleared 后,后续指令能够顺利进入流水线而不受后端 queue / buffer backpressure 影响。Writeback queue 满、Result Queue 溢出、memory request queue 满都可能产生 backpressure。
5.8.2 Observable Stall Symptom Table
以下症状对应 GPU profiler 中可观察到的指标,可用于将性能问题映射到具体硬件路径。
Execution path symptoms
| 症状 | 可能原因 |
|---|---|
| Eligible warp / wavefront 数量低 | Dependency stall 严重、memory latency 高、barrier 等待长 |
| Issue active 低,但 pipe busy | Warp 数量不足、branch divergence 严重、active lane 比例低 |
| Pipe busy 但 active lane 利用率低 | Branch divergence、exec mask 覆盖率高、partial warp |
| Branch efficiency 下降 | Conditional branch 频繁 divergence、reconvergence 延迟 |
Register / Local path symptoms
| 症状 | 可能原因 |
|---|---|
| Occupancy 低于理论上限 | Register pressure 过高、Local Shared Storage 分配过多、Workgroup 配置不当 |
| Spill / scratch traffic 增加 | Register allocation 失败、live range 过长、loop unroll 过度 |
| RF bank conflict stall | 多条指令同时访问同一 RF bank、operand address 分布不均 |
| Operand collector stall | RF read port pressure 高、短程 reuse 不足、bank conflict 积累 |
| Local Shared Storage bank conflict | 访问 pattern 导致多 lane 映射到同一 bank、stride 为 bank 数量整数倍 |
Memory path symptoms
| 症状 | 可能原因 |
|---|---|
| Cache hit rate 下降 | 访问 pattern 缺乏 locality、working set 超过 cache capacity、伪共享 |
| L2 traffic 增加 | L1 miss 率高、coalescing 效率低、多个 Workgroup 竞争 L2 bandwidth |
| TLB miss / page walk 增加 | 大工作集、稀疏访问、page residency 管理不当 |
| Memory dependency stall 增加 | Variable-latency operation 占比高、memory return 队列长 |
| Texture / Sampler path stall | Texture cache miss、sampler state pressure、descriptor lookup 延迟 |
Pipeline coupling symptoms
| 症状 | 可能原因 |
|---|---|
| Queue / buffer backpressure | 下游阶段处理速度低于上游发射速度、result queue 满、memory queue 满 |
| Cross-stage stall | EU 完成速度快但 writeback 慢、 LSU 发射快但 cache 响应慢 |
| Occupancy 高但 utilization 低 | 所有 warp 同时等待同一资源(cache miss 风暴、barrier 后同步释放) |
这些症状不是独立的。实际性能问题通常是多路径叠加:高 register pressure 降低 occupancy,低 occupancy 削弱 latency hiding 能力,variable-latency memory operation 因无法被充分隐藏而拉长 eligible warp 等待时间,最终表现为 EU utilization 持续低迷。定位瓶颈需要逐级检查 inequality chain 的各环节,找到压力最大的约束点。
六、总结与展望
这一篇从 Graphics API 的软件契约出发,经 Graphics Driver 的翻译与编译层,逐层进入 Architecture 与 Microarchitecture 的硬件实现,建立一个跨厂商的 GPU 分析框架。这个框架的核心是一条从软件抽象到硬件路径的追踪链,而非名词集合:每一个暴露给开发者的 Feature 或 API 概念,都对应着具体的物理结构、数据移动方式、依赖跟踪机制与可观察的性能行为。
全篇的隐含主线是一次思维方式的转换:从"这个 Feature 能做什么"转向"这个 Feature 改变了哪条硬件路径、引入了哪种依赖、转移了哪类瓶颈"。
6.1 全篇概览
全篇的组织逻辑遵循一条由软到硬的层次链,每一层都为下一层提供必要的抽象契约与约束条件。
Graphics API 层阐述了现代显式 API(Vulkan、DirectX 12、Metal)的核心契约:显式内存管理、显式同步屏障、命令提交与 Pipeline State Object。这些 API 把更多控制权交给开发者,同时也把更多责任转移过来。Barrier 不是单一 flush,它是一组关于可见性、可用性与执行顺序的约束集合。Render Pass 不只是绘制流程容器,它是 attachment lifetime 与 Pixel Backend ownership 的契约。Descriptor 也不只是绑定表,它是 resource lookup 的硬件映射约定。理解这些契约的边界,是避免"把 API stage 直接当成硬件单元"的前提。
Graphics Driver 层分析了驱动作为 API 契约到硬件命令流翻译者的角色。User Mode Driver 负责 shader 编译、寄存器分配、指令调度、命令缓冲构建。Kernel Mode Driver 负责内存驻留管理、分页、硬件提交与进程隔离。Driver 与 Compiler 共同决定了 shader source 如何经由 register allocation、scalarization、vectorization、spill decision、dependency placement 等步骤,最终变成 command stream、native ISA、pipeline state 与 synchronization primitive 的硬件映射。Native ISA 本身也远不止 opcode,它可能包含 predicate、dependency hint、reuse flag、wait instruction、barrier、memory scope、cache operator 等控制信息,这些信息决定了 hardware 实际执行的行为。
Architecture 层将 GPU 组织为一个分层执行结构:Device → Shader Array / Cluster → Macro-Core → SIMD Partition → Logical Warp → Vector Lane → Invocation。这个层次覆盖了从全局工作分发到单个执行通道的粒度跨度。Memory Subsystem 被分解为多条可区分的数据路径(Global LD/ST Path、Texture / Sampler Path、Constant / Uniform Path、Local Shared Storage Path、Tile-local Storage Path、Pixel Backend Write Path、RT Traversal Memory Path、Matrix Operand Movement Path),每条路径在地址来源、请求合并粒度、缓存层级、依赖解除方式与 stall 触发条件上都有差异。PS render target write 与 CS UAV write 走不同的物理路径,这一区分是理解 barrier cost、layout transition 与 compression state 差异的基础。
Microarchitecture 层聚焦于执行模型与延迟管理机制。GPU 的主路径是多 Logical Warp 并发、深流水、轻量 Dependency Tracking 与交织执行,走的是与 CPU 风格 OoO(Out-of-Order)完全不同的路线。这一选择的工程动因在于 GPU 的并行规模使得全端口 Register File 在面积和功耗上不可行,OoO 所需的 rename register、issue window、reorder buffer 等结构无法承受数百个并发 warp 的端口需求。GPU 走了另一条路线:用 Thread-Level Parallelism 掩盖 latency,用轻量的 Dependency Tracking 管理指令就绪状态。
Dependency Tracking 不是单一机制。NVIDIA 采用 hardware-managed scoreboard 控制 Logical Warp 的 issue eligibility。AMD 通过 compiler 插入的 waitcnt 配合 hardware dependency counter 管理 wavefront 的就绪状态。Apple / mobile GPU 通过不公开的内部机制处理 memory return、barrier、tile-local drain 与 pipe backpressure。这些实现差异放在同一个 Dependency Tracking 分析框架下才是可比较的。Register File 的多 bank 组织、Operand Collector 的仲裁、LSU 的 coalescing 与 variable-latency return,共同构成了高吞吐执行的基础约束。固定延迟依赖(如 ALU-ALU)与可变延迟依赖(如 memory return、cache miss、TLB miss)的区分,是理解 compiler scheduling 与 runtime tracking 分工的关键。
Screen-space Work Organization 的分析替代了传统的 IMR/TBR 二分,沿三个维度展开:几何与屏幕空间工作由谁分发(Work Distribution);pixel work 是否经过 tile / bin / cache-aware 组织(Pixel Work Organization);最终 color/depth/stencil 状态由谁持有(Final Pixel State Ownership)。这三个维度同时容纳了 NVIDIA GPC/ROP-L2/tile caching、AMD SE/RB/DCC/Infinity Cache、以及 Apple TBDR/Tile Memory/Imageblock 等不同路线。
6.2 未来展望:共性瓶颈与架构响应
GPU 的用途正在分化。图形渲染、AI 训练与推理、科学计算、实时模拟对硬件提出不同的优化需求。但无论 workload 如何分化,若干共性瓶颈持续存在,硬件与软件的演进也围绕这些瓶颈展开。
全章核心判断:未来 GPU 的共同约束是 data movement、memory capacity、coherence、packaging 与 networking 的复合压力,单纯算力不足已退居次要。单个 Macro-Core 的 ALU throughput 持续增长,但数据供给能力(memory bandwidth、cache capacity、cross-die / cross-package interconnect 带宽)增速相对滞后。API 与 driver 的变化只是这些物理约束在软件层的投影。
6.2.1 共同主瓶颈:Memory Capacity / Bandwidth / Movement
计算复杂度持续提升,数据量也在增长。Memory capacity、bandwidth 与数据移动成本已成为制约 GPU 性能的共同主瓶颈。图形渲染的纹理与几何数据、AI 训练的模型参数与激活值、科学计算的网格数据,都需要在 device memory 与片上存储之间搬运。bandwidth 的增长速度持续落后于 compute throughput 的增长,这一差距定义了 memory wall 的长期约束。数据移动不仅消耗带宽,还消耗能量:跨层级、跨 die、跨 package 的数据搬运在功耗预算中占比越来越大。
6.2.2 硬件响应:封装、互联与存储技术
HBM4 存储接口
JEDEC 于 2025 年 4 月正式发布 HBM4 标准(JESD270-4)。按其官方发布文本:接口位宽提升至 2048-bit(较 HBM3 的 1024-bit 翻倍),单堆栈独立通道数从 16 增至 32,pin 速率最高 8 Gb/s,对应单堆栈约 2 TB/s 的接口带宽。堆叠规格覆盖 4 至 16 层、24 Gb 与 32 Gb die 密度,单堆栈最高容量 64 GB。HBM4 同时引入 Directed Refresh Management(DRFM),强化了 RAS(Reliability, Availability, Serviceability)功能与 row-hammer 防护。从架构分析角度看,HBM4 在接口位宽、通道密度与容量三个维度扩展了存储子系统的上限,但并未改变"片上 compute 与片外 storage 之间的物理距离"这一根本约束。HBM 的堆叠封装减少了 PCB 级走线延迟,但 memory controller、interconnect fabric、cache hierarchy 仍然位于数据路径上,每个环节都贡献延迟与功耗。
Coherent CPU-GPU Memory Pool
以 NVIDIA Rubin 平台公开的 second-generation NVLink-C2C 为例,NVIDIA 官方 Vera 页面写明其提供最高 1.8 TB/s 的 coherent bandwidth,承担 Vera CPU 与 Rubin GPU 之间的一致性互联。在该架构描述中,CPU 侧 LPDDR5X 与 GPU 侧 HBM4 被组织为一个统一的一致性内存池(coherent memory pool)。CPU 与 GPU 由此可直接访问共享地址空间,异构计算中数据移动的成本结构随之改变。对开发者而言,统一内存池减少了显式数据搬运,但增加了对 coherence protocol、cache state 与 access latency 差异的理解要求。一致性的实现有代价:snoop traffic、cache line invalidation、write-back 等操作会消耗额外的 fabric bandwidth 与能量。
UCIe 与 Chiplet 架构
UCIe(Universal Chiplet Interconnect Express)提供了标准化的 die-to-die interconnect 规范,覆盖物理层、协议栈、软件模型与一致性测试。该技术允许构建超越 reticle size 的多 die SoC,并降低多厂商 chiplet 组合的工程门槛。对 GPU 而言,chiplet 设计意味着 Macro-Core、memory controller、I/O 等功能模块可在不同 die 上实现,通过 die-to-die link 互联。这引入了新的数据路径变量:跨 die traffic 需要经过 UCIe 物理层与协议栈,其延迟、带宽与功耗特性与同 die 内连接不同。一致性协议需要处理跨 die 的 cache coherency 与 ordering 问题。Chiplet 带来的灵活性增益需要在分析中与跨 die 数据移动成本一起权衡。
Photonics / CPO 的现实落点
CPO(Co-Packaged Optics)与硅光子技术在 2025-2026 年的公开落点集中在 networking 与 switch 侧。NVIDIA Spectrum-X / Quantum-X 等产品方向表明,光互联的初始应用场景是机架间与集群间的高带宽互联,而非直接替代 GPU 芯片内部的电信号路径。从架构分析角度看,性能瓶颈的关注范围需要从单芯片扩展到 rack、cluster 与 fabric 层面。光互联解决的是大规模分布式系统中电气互联的带宽密度与功耗约束,电信号在长距离传输中的衰减与串扰限制了机架级互联的带宽上限,光子信号不受此限。但在芯片内部,电信号互联在延迟与集成度上仍具优势。光互联与电互联将在不同层级上共存,分析者需要根据具体的 data movement 范围选择对应的互联模型。
6.2.3 软件抽象响应:Work Graph 与 API 演进
Work Graph
Work Graph 做的事情是让 API 重新吸纳"复杂调度与数据流组织"的能力。它允许开发者以有向无环图(acyclic graph,允许 self-loop)的形式定义 GPU 上的任务依赖与数据流,runtime 负责 node 间的数据搬运与同步。这是一次控制权转移:CPU 不再承担所有调度决策,GPU 获得了更大的自主调度空间。
但约束条件不可忽视:graph depth 有限制。node 间通信由 runtime 管理,开发者不直接控制跨 node 数据搬运的物理路径。resource lifetime 与 pipeline pressure 的映射关系仍然存在,只是从显式 barrier 转换为 graph edge 的隐式依赖。Work Graph 重新吸纳调度能力的同时,也把调度的复杂性收回了 runtime。开发者需要理解这种新抽象下的 dependency model,而不是假设 GPU 自主性消除了所有性能约束。
Vulkan Roadmap 2026
Khronos 于 2026 年初发布 Vulkan Roadmap 2026 milestone,包含 VK_EXT_descriptor_heap、VRS(Variable Rate Shading)、shader clock queries、host image copies、compute shader derivatives、swapchain improvements 以及更高的 descriptor / shader interface limits 等特性。这一更新的分析意义在于 API 的公约数正在向高端硬件能力上移。新扩展持续引入更底层的硬件控制能力,开发者需要理解的概念链条更长:每个新 capability 背后都有对应的 physical path、data movement pattern 与 dependency model。
上述 API 演进信息具有时效性。具体扩展名称、版本号与特性集合会随时间变化,但"API 抽象持续向硬件路径靠拢"这一趋势具有持续性。
6.3 如何使用本分析框架审查后续厂商篇
上面建立的分析框架是一条可重复的追踪链条,不是一套需要背诵的名词清单。后续厂商篇中遇到具体实现时,建议按以下链条审查每个 Feature:
Feature / API Abstraction
→ Physical Structure(哪些硬件单元参与,位于哪个层级)
→ Data Movement(数据走哪条路径,经过哪些缓存与互联层级)
→ Dependency Tracking(可变延迟依赖如何管理,固定延迟依赖如何处理)
→ Pipeline Pressure(该 Feature 对哪些执行管线、存储端口或互联通道施加压力)
→ Observable Stall(当压力超过容量时,在 profiler 上表现为哪些症状)
→ Programming Consequence(开发者应如何调整代码以适配硬件约束或规避瓶颈)
这条链条的价值在于通用性:它不预设某个厂商的实现为默认模型,也不把不同路线当作互不兼容的特例。每个环节都对应可观察、可测量、可比较的工程属性。
该链条的应用方式可通过后续厂商篇的实例化说明来理解:
Vol.2 NVIDIA:
Hardware-Managed Scoreboard / SASS control field / SM (Macro-Core)
/ Logical Warp / Warp Scheduler / Matrix Acceleration Path
/ RT Traversal Path / ROP-L2 Pixel Backend
是该分析框架的一种实现。其核心特征是将大量依赖跟踪信息暴露给 compiler,
同时用 hardware scoreboard 管理运行时 eligibility。
Vol.3 AMD:
SALU/VALU 分裂 / EXEC mask / waitcnt dependency counter
/ Local Shared Storage (LDS) / RB/DCC Pixel Backend
/ Infinity Cache (Large Last-level Cache)
是同一框架下的另一种实现。其核心特征是将部分可变延迟依赖显式化为
ISA 级别的 waitcnt,由 compiler 负责 dependency placement。
Vol.6 Apple:
TBDR / Tile-local Storage / UMA / SLC (System-level Cache)
/ imageblock / Render Pass ownership / clause-like execution
/ opaque hardware tracking
是 SoC/TBDR 路线的实现。其核心特征是将多数调度与依赖细节隐藏在
硬件内部,通过 API 契约(Render Pass load-store 语义、Tile Memory 生命周期)
间接控制,并把 GPU 放入 SoC 级 UMA、Memory Fabric 与热预算约束中理解。
不同厂商在同样的分析框架槽位中填入了不同的工程选择。NVIDIA 倾向于将控制信息暴露给 compiler,以获得更精细的静态调度。AMD 选择在 ISA 层面显式化部分依赖,compiler 因此拥有更多决定权。Apple 选择将复杂性隐藏在硬件与 runtime 内部,通过更高层级的 API 契约间接表达。这些差异不是"谁更先进"的问题,而是不同产品定位、生态策略与约束条件下的工程取舍。
上述分析框架提供了足够中性的槽位,使这些差异可以被比较和分析。后续厂商篇将在这条追踪链上填入各自的具体实现、性能特征与编程约束。
参考文献
- Apple Developer Documentation, "MTL4CommandEncoder — barrier(afterEncoderStages:beforeEncoderStages:visibilityOptions:)",https://developer.apple.com/documentation/metal/mtl4commandencoder/barrier(afterencoderstages:beforeencoderstages:visibilityoptions:)(访问日期:2026-07-18)
- AMD ROCm Documentation, "User Guide for AMDGPU Backend — LLVM",https://rocm.docs.amd.com/projects/llvm-project/en/docs-7.0.2/LLVM/llvm/html/AMDGPUUsage.html(访问日期:2026-07-18)
- NVIDIA, "CUDA C++ Programming Guide — Hardware Implementation / SIMT Architecture",https://docs.nvidia.com/cuda/cuda-c-programming-guide/(访问日期:2026-07-18)
- Chips and Cheese, "Microbenchmarking Nvidia's RTX 4090",https://chipsandcheese.com/p/microbenchmarking-nvidias-rtx-4090(访问日期:2026-07-18)
- Chips and Cheese, "Microbenchmarking AMD's RDNA 3 Graphics Architecture",https://old.chipsandcheese.com/2023/01/07/microbenchmarking-amds-rdna-3-graphics-architecture/(访问日期:2026-07-18)
- JEDEC, "JEDEC and Industry Leaders Collaborate to Release JESD270-4 HBM4 Standard",2025-04-16,https://www.jedec.org/news/pressreleases/jedec%C2%AE-and-industry-leaders-collaborate-release-jesd270-4-hbm4-standard-advancing(访问日期:2026-07-18)
- NVIDIA, "NVIDIA Vera CPU — NVLink-C2C",https://www.nvidia.com/en-us/data-center/vera-cpu/(访问日期:2026-07-18)
