移动 GPU 基础概述

Arm GPU Training

"It's about getting the prettiest pixels from a system when you have less than 3 Watts to spend"

移动 GPU 的主要用途

1. 3D 游戏

  • 传统认知中 GPU 的核心用途,延续自桌面 PC 和游戏主机
  • 移动端 3D 游戏市场持续增长,如 FortnitePUBG王者荣耀
  • 这类游戏专门针对移动端优化,追求高品质画面

2. 2D 游戏

  • 实际上,Google Play 商店中约 75% 的游戏是 2D 游戏
  • 目标设备跨度大:从入门级手机到旗舰机
  • 虽然图形简单,但要在低端设备上流畅运行仍面临 性能挑战

3. 操作系统 UI 渲染

  • 几乎所有移动平台的 系统 UI 都由 GPU 渲染
  • Android 示例:
    • 主屏幕 = 原生 3D 应用
    • 动态壁纸 = 原生 3D 应用
    • 系统合成器实时合成所有内容
  • 同样适用于:网页浏览器加速、普通应用的 UI 框架
  • 关键结论 :GPU 必须在简单 3D 图形上做到 极致能效 ,因为这类负载 全天候运行

4. 视频与摄像头集成

  • 应用场景:机顶盒(卫星/有线解码)、智能手机摄像头
  • GPU 需要高效处理 视频流和相机图像
  • Mali GPU 可直接访问 YUV 编码数据 ,而非仅限传统 RGB 格式

5. 通用计算(GPGPU)

GPU 本质是 数据并行处理器 ,图形只是其擅长的并行问题之一。

应用领域说明
计算机视觉对图像/传感器数据运行并行算法,推断物体分类或语义
机器学习神经网络归结为大规模 矩阵乘法 ,GPU 极其擅长

移动系统渲染管线概览

基本数据流

CPU(应用 + 图形驱动)
        ↓
    命令 + 资源(纹理、着色器、数据)
        ↓
       GPU
        ↓
   完成的窗口表面(帧缓冲)
        ↓
    显示处理器(Display Processor)
        ↓
      屏幕输出

移动端 vs 桌面端的关键区别

方面桌面 PC移动端
显示处理器集成在 GPU 内部独立 IP 模块 ,通常由不同公司开发
Mali GPU 职责仅负责渲染,将窗口写回 主内存
显示输出GPU 直接驱动由另一个硬件模块从内存读取并输出到屏幕

核心概念 :移动端 GPU(如 Mali)与显示处理器解耦,GPU 渲染结果先写回内存,再由显示模块读取——这一架构对理解后续的 Tile-Based Rendering 至关重要。

GPU 自主命令生成能力

现代 GPU 的自主性演进

资源自生成

  • GPU 可以 渲染到纹理(Render to Texture),实现复杂的渲染管线
  • 支持 有趣的后处理效果,全部由 GPU 自行完成

命令流修改

技术说明
Draw IndirectGPU 可修改 CPU 提交的任务参数
Multi-Draw Indirect批量间接绘制,进一步减少 CPU 干预
  • 当前限制:API 尚未完全通用化,GPU 只能填充 CPU 提供的 绘制模板 的部分字段
  • 发展趋势:GPU 自主命令提交能力持续增强,CPU 可卸载更多工作,降低 CPU 负载

桌面 vs 移动:功耗鸿沟

桌面游戏 PC 的功耗特征

参数典型值
系统功耗200–400 瓦 (CPU + GPU + DRAM)
散热能力80–160 CFM(立方英尺/分钟)气流
散热手段大型散热片、水冷、多风扇

核心原理:每瓦输入功率 = 等量废热输出。高功耗必须匹配强力主动散热。

移动设备的物理约束

SoC 封装结构

┌─────────────────┐
│     DRAM Die    │  ← 内存芯片
├─────────────────┤
│   Logic Die     │  ← CPU、GPU、加速器
│ (CPU+GPU+Accel) │
└─────────────────┘
    Package-on-Package (PoP)
  • 优点:PCB 简化、布线更少、效率更高
  • 致命缺点:所有发热大户 集中在一处

散热劣势

因素影响
无主动散热无风扇、无散热片
外壳材质玻璃/塑料 = 热绝缘体
保护壳皮革/塑料壳进一步隔热

移动端热预算

  • 仅为桌面 PC 的约 1%
  • 形象比喻:比一个 5W LED 节能灯泡 还要少一半!

移动 SoC 渲染子系统架构

CPU 异构设计(big.LITTLE 及其演进)

核心类型

类型特点数量(典型)
Big 核物理尺寸大、 单线程性能最高1–2
Medium 核性能/能效平衡2
Little 核小巧、 最省电 、峰值性能低4–6
Prime 核(最新)单核极高频率,专攻极端单线程负载1

调度策略

  • 操作系统根据 负载需求 动态分配任务:
    • 轻负载 → Little/Medium 核
    • 重单线程负载 → Big/Prime 核

缓存层级

  • L3 缓存:所有 CPU 核心共享,提供一致性后端
  • System Cache:位于所有 IP 与 DRAM 之间(取决于芯片厂商设计)

显示处理器(Display Processor)

为何叫"处理器"而非"控制器"

现代显示处理器具备 独立图像处理能力

功能说明
旋转画面方向调整
缩放分辨率变换
多层合成例如 Android 主屏图标层 + 背景壁纸层
色彩转换YUV → RGB 等

硬件限制与回退机制

显示处理器受 硬实时约束(必须匹配屏幕刷新率),当任务超出能力时:

  • 层数过多
  • 分辨率过高
  • 需要过多旋转/色彩转换

回退到 GPU 软件合成,生成扁平化表面再交给显示控制器

Vulkan 中的注意事项

  • 应用直接控制 屏幕旋转 等参数
  • 最佳实践:确保提交给显示控制器的内容在其能力范围内
  • 代价:不当配置导致 GPU 回退合成 → 浪费宝贵的功耗预算在无附加值的工作上

DRAM 访问:移动端最大的功耗黑洞

内存带宽的能耗代价

经验公式

  • 误差范围约 ±30%(取决于制程和 DRAM 技术)
  • 示例:5 GB/s 流量 ≈ 消耗 0.5W

实际约束

总功耗预算可用 DRAM 带宽上限
2–3 W5–7 GB/s(持续)

超出此范围会挤占 CPU/GPU 的功耗预算。


完整 SoC 功耗分配示例

各模块竞争同一预算

┌─────────────────────────────────────┐
│    Total Power Budget: 2.5 W        │
├────────────────┬────────────────────┤
│ DRAM Bandwidth │ 0.5 W  (≈5 GB/s)   │
│ CPU            │ 0.75 W             │
│ GPU            │ 1.25 W             │
│ DPU (Display)  │ Low power, but     │
│                │ bandwidth counted  │
│                │ under DRAM         │
└────────────────┴────────────────────┘

域专用加速器(共享功耗)

加速器功能
Modem语音/数据通信 + 射频 IO
Video Codec视频编解码
ISP相机原始数据处理
NPU/DSP机器学习推理
GPS定位

关键洞察:玩《Pokémon GO》时,相机 + GPS + 网络同时工作,可能吃掉 0.5–1W,留给渲染的预算骤降!


频率-电压-功耗曲线:移动优化的核心

典型 Big Core 功耗曲线

功耗 (W)
   ↑
   │            ╱ 超频区(陡峭)
   │          ╱
   │        ╱ ← 标称频率(Nominal)
   │      ╱     ~750 mW @ 14 性能点
   │    ╱
   │  ╱  降频区(平缓)
   │╱
   └─────────────────────→ 性能
工作点特性
超频曲线陡峭 → 性能微增,功耗暴涨
标称设计参考点(如 0.75V)
降频曲线平缓 → 性能略降,能效显著提升

能效视角:为何要多核?

能效 = 性能 / 功耗

频率绝对性能能效(性能点/瓦)
最高频最高~13
标称频14~18
半频7~35(提升 ~95%!)

结论

为何手机有 8 核:多线程并行 → 每核可降频运行 → 整体能效更高


效率 vs 效能:优化的哲学

Peter Drucker 的经典论断

"Efficiency is doing things right. Effectiveness is doing the right things."

效率是把事情做对,效能是做对的事情。

应用于图形优化

误区正确思路
死磕 Shader 微优化先审视 算法是否正确
榨干现有管线考虑 管线架构本身 是否合理

错误的算法实现得再好也没用——优化要从全局视角出发。


渲染管线速览

三阶段流程

┌────────┐     ┌───────────┐     ┌──────────┐
│ Vertex │ ──→ │ Rasterize │ ──→ │ Fragment │
└────────┘     └───────────┘     └──────────┘

1. 顶点处理

  • 输入: 网格(Mesh) ——物体的"无限薄表皮",内部是空的
  • 任务:每帧将每个顶点 动画/变换 到屏幕位置

2. 光栅化

  • 将三角形的 矢量形式 转换为 像素覆盖
  • 决定哪些像素需要着色

3. 片元着色

  • 对每个光栅化后的像素运行 Fragment Shader
  • 应用材质、光照、纹理等 → 最终画面效果

图形渲染管线详解

管线核心思想

顶点输入 ──→ 几何处理 ──→ 光栅化 ──→ 片元着色 ──→ 帧缓冲输出
   │              │              │            │
 大量顶点      屏幕空间三角形    像素覆盖     像素上色

核心目标:尽可能快地处理海量顶点、填充海量像素。移动端通常只使用基础阶段。


完整管线架构

四大模块

模块职责
CPU应用逻辑、提交命令
命令处理器解析驱动发来的 GPU 指令
几何处理网格数据 → 屏幕空间三角形
像素处理屏幕三角形 → 像素着色

资源上传流程

1. 上传资源
   ├─ 着色器程序
   ├─ 缓冲区 / 纹理
   ├─ 参数(混合开关、深度测试等)
   └─ 描述符集(Descriptor Set)
      描述符 = C 结构体
      └─ 资源位置指针
        ↓
2. CPU 提交 Draw Call
   "用描述符集 1 绘制 N 个三角形"

典型渲染帧

  • 每帧约 1000 次 Draw Call
  • 每次 Draw Call:命令处理 → 几何处理 → 像素处理

计算着色器(Compute Shader)

独立于渲染管线

	┌─────────────────┐
	│   Main Memory   │
	│  (Input/Output) │
	└─────────────────┘
             ↓
	┌─────────────────┐
	│  Compute Shader │  ← 通用并行处理
	└─────────────────┘
             ↓
      Buffer / Texture
  • 输入:可来自任意阶段
  • 输出:可被任意阶段读取
  • 特点:不属于固定功能管线,但 极其重要

几何处理阶段详解

阶段构成

颜色标识含义
绿色应用程序可编程(Shader)
蓝色固定功能,仅可配置参数

各阶段功能

1. 顶点着色器(Vertex Shader)

  • 职责:物体空间 → 屏幕空间变换 + 逐顶点计算
  • 必经阶段,无法跳过

2. 曲面细分(Tessellation)

特性说明
结构2 个着色阶段 + 1 个固定功能细分器
功能将粗糙 Patch 细分为更多三角形
优点可改变物体 轮廓边缘
缺点非常昂贵,难以正确使用
移动端现状几乎不用(仅在 Benchmark 中见过)

⚠️ 曲面细分与 Tile-Based 架构 配合不佳

3. 几何着色器(Geometry Shader)

特性说明
功能每次调用可输出 多个图元
历史用途粒子系统、爆炸、烟雾等
现代建议不要使用!

为何弃用?

  • 设计缺陷导致 三角形重复
  • 效率极低
  • 替代方案:用 Compute Shader 实现相同功能

移动端实际内容中 从未见过 几何着色器(仅 Benchmark)


管线优化:裁剪提前

规范定义 vs 实际实现

问题:OpenGL/Vulkan 规范将 Culling 放在最后

顶点着色 → 曲面细分 → 几何着色 → 图元装配 → 【裁剪】
                                              ↑
                                         所有工作都做完了,
                                         才发现三角形不可见!

Mali 现代架构优化

image.png

顶点着色器拆分:                            
  Part 1: 仅计算 Position            
              ↓                           
       【裁剪 & 剔除】 ← 提前执行!        
              ↓                    
  Part 2: 计算其他逐顶点数据(仅对可见三角形)

效率收益

场景收益
良好应用(~50% 在视锥外)Part 2 只跑 一半顶点
较差应用(更多视锥外物体)收益 更大

这是 通用硬件优化,非 Mali 独有。核心原则:裁剪越早越好


像素处理阶段

阶段构成

image.png

ZS 测试双层结构

测试时机作用
Early ZS片元着色 之前尽早剔除不可见片元,节省着色开销
Late ZS片元着色 之后处理 Shader 中修改深度的情况

关键优化点:尽量让 Early ZS 生效,避免无效着色。

像素处理阶段详解

光栅化(Rasterization)

基本原理

  • 矢量三角形 转换为 离散采样点(像素)
  • 测试三角形覆盖了哪些像素采样点

Quad:2×2 像素块

┌───┬───┐
│ P │ P │  ← 一个 Quad = 4 个像素
├───┼───┤     始终以此为最小单位处理
│ P │ P │
└───┴───┘

为何必须用 Quad?

  • 提供 导数信息
  • 用于 Mipmap 级别选择(纹理采样需要知道像素间变化率)

小三角形的效率问题

覆盖情况实际像素消耗资源
完整 Quad44
部分 Quad(仅 1 像素)14(浪费 75%!)

示例

三角形实际覆盖:10 像素
因 Partial Quads:消耗 20 像素的资源
               → 50% 效率损失

核心结论小三角形代价极高

  • 顶点数据更多(覆盖相同面积需更多三角形)
  • 顶点开销摊分到更少片元
  • Partial Quads 导致 线程利用率下降

深度/模板测试(ZS Test)

Early ZS(早期深度测试)

光栅化 ──→ ██ Early ZS ██ ──→ 片元着色
              ↓
         被遮挡片元提前剔除
条件效果
前向后渲染 不透明物体遮挡片元被 直接跳过 ,省去着色开销
后向前渲染Early ZS 失效 → 严重 Overdraw

最佳实践:不透明物体 从近到远 排序渲染!

Late ZS(后期深度测试)

何时需要 Late ZS?

  • 片元着色器 写入深度值gl_FragDepth
  • Alpha Test(如树叶镂空)
// 示例:Alpha Test
void main() {
    float alpha = texture(tex, uv).a;
    if (alpha < 0.5) discard;  // ← 此时才知道是否丢弃
    // ...
}

驱动优化:提前触发 Late ZS

image.png

片元着色器                                
 ├─ 读取 Alpha                         
 ├─ 判断 discard ──→ 触发 Late ZS ──→ 剔除 
 └─ 剩余计算(若被剔除则跳过)      

无需等整个 Shader 跑完,尽早剔除 减少浪费。


隐藏面消除(Hidden Surface Removal, HSR)

微架构级优化

  • 非标准化:各 GPU 厂商自有实现(Arm、Qualcomm、Apple 等)
  • 目标:消除 Early ZS 无法架构性处理的遮挡
Early ZS 的局限HSR 的补充
依赖渲染顺序可在更早阶段剔除
无法处理乱序提交硬件级重排/缓存

管线并行与瓶颈分析

理想流水线

时间 →
┌─────────────────────────────────────────────────┐
│ CPU:  [DC1][DC2][DC3][DC4][DC5]...              │
│ Geom:      [DC1][DC2][DC3][DC4]...              │
│ Pixel:          [DC1][DC2][DC3]...              │
└─────────────────────────────────────────────────┘
      ↑
   稳态:三阶段并行工作

好处

  • 硬件利用率最大化
  • 可在 最低频率 下运行 → 最佳能效

管线阻塞:glFinish 的灾难

时间 →
┌─────────────────────────────────────────────────┐
│ CPU:  [DC1]█████████████[DC2]█████████████...   │
│ Geom:      [DC1]             [DC2]              │
│ Pixel:          [DC1]             [DC2]         │
└─────────────────────────────────────────────────┘
         ↑                    ↑
      glFinish            glFinish
      (CPU 等待)          (CPU 等待)
问题影响
管线排空CPU 必须等待所有 GPU 工作完成
串行执行无并行 → 吞吐量骤降

避免glFinish 及类似的同步调用(除非调试)


瓶颈识别

场景:CPU Bound

时间 →
┌─────────────────────────────────────────────┐
│ CPU:  [██████][██████][██████][██████]      │  ← 100% 忙碌
│ GPU:  [  ][  ][  ][  ][  ][  ][  ][  ]      │  ← 出现空闲
└─────────────────────────────────────────────┘

诊断

  • CPU 始终满载
  • GPU 在几何/像素处理间出现 空闲间隙
  • 结论:CPU 是瓶颈,优化重点在应用层

瓶颈类型总结

瓶颈位置表现优化方向
CPU BoundGPU 有空闲减少 Draw Call、批处理、多线程命令录制
Geometry Bound像素阶段等待减少顶点数、简化顶点着色器
Pixel Bound几何阶段等待降分辨率、简化片元着色器、减少 Overdraw
带宽 Bound全阶段都慢压缩纹理、减少 Render Target 切换

管线瓶颈识别

核心原则

  • 其他阶段在最慢阶段的"阴影"中并行运行
  • 优化方向:找到并解决瓶颈阶段

工具价值:Mobile Studio 等性能分析工具的核心能力就是 识别瓶颈阶段


GPU 架构概览

核心处理模块

image.png

关键设计问题

问题影响
后变换缓冲几何输出存哪?如何传给像素阶段?
帧缓冲工作集ZS 测试、混合、MSAA 的热数据存哪?

厂商差异点:不同 GPU 的"胶水"设计是主要区分因素


立即模式渲染器(Immediate Mode Renderer, IMR)

历史背景

  • 软件渲染器时代(80 年代末–90 年代初)的经典设计
  • 代表硬件:3DFX Voodoo(早期消费级 3D 显卡)

工作原理

处理顺序

Draw Call 1: 三角形 1 → 2 → 3 → ...
Draw Call 2: 三角形 1 → 2 → 3 → ...
      ↓
严格按应用提交顺序逐个处理

关键特征

任意三角形可触及屏幕任意位置

  • 无空间局部性假设
  • 渲染绿色条纹时,需重新加载已渲染的蓝色背景区域

IMR 管线结构

image.png

FIFO 缓冲机制

情况行为
FIFO 满几何处理 暂停 ,等待空间
FIFO 有空间几何处理继续

帧缓冲存储

  • 帧缓冲 必须存于主内存(数十 MB:颜色 + 深度 + 模板)
  • 每个三角形可能访问任意位置 → 频繁 内存往返

IMR 的权衡

方面成本
三角形便宜 — 数据留在片上 FIFO
像素昂贵 — 反复访问主内存

像素成本来源

  • ZS 测试:读取深度值
  • 混合:读取目标颜色
  • MSAA:多采样点读写

可通过 帧缓冲压缩缓存 缓解,但无法根本消除


IMR 的延迟特性

image.png

Time →
┌──────────────────────────────────────────────────────────┐
│ Draw Call:  [Submit]                                     │
│ Geometry:        [Process]                               │
│ FIFO:                 [First Triangle Enqueued]          │
│ Pixel:                     [Start Immediately]           │
└──────────────────────────────────────────────────────────┘

优势

  • 极低延迟:首个三角形进入 FIFO 即可开始像素处理
  • 紧密流水:几何与像素阶段高度重叠
  • 甚至 同一 Draw Call 内部 也能重叠

小结:IMR 适用场景

优势劣势
低延迟、紧密流水帧缓冲带宽消耗大
三角形开销低多层渲染时 Overdraw 严重

下一节预告:Tile-Based Rendering(TBR)如何解决 IMR 的带宽问题

分块渲染器(Tile-Based Renderer, TBR)

核心设计思想

               Screen Divided into Tiles          
  ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐  
  │16×16│     │     │     │     │     │     │     │  
  ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤  
  │     │     │     │     │     │     │     │     │  ← 每个Tile独立渲染至完成
  ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤  
  │     │     │     │     │     │     │     │     │  
  └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘  

Mali GPU 使用 16×16 像素 Tile(其他厂商 Tile 尺寸可能不同)


两阶段分离管线

image.png

┌───────────────┐         ┌─────────────┐         ┌───────────────┐
│   Geometry    │         │             │         │     Pixel     │
│  Processing   │──Write─→│ Main Memory │──Read─→ │   Processing  │
│ (All Finished)│         │             │         │   (Per Tile)  │
└───────────────┘         │• Vertex Data│         └───────────────┘
                          │• Tile List  │                 ↓ 
                          └─────────────┘         ┌───────────────┐    
                                                  │   Tile RAM    │
                                                  │ (On-chip HW)  │
                                                  └───────────────┘

Tile List 结构

Tile 坐标关联三角形 ID
(0, 0)△1, △5, △23, ...
(0, 1)△2, △5, △8, ...
......

必须先完成全部几何处理 → 才能知道每个 Tile 涉及哪些三角形


Tile RAM:像素效率的关键

存储内容

数据类型说明
颜色RGBA
深度Z 值
模板Stencil
多采样MSAA 采样点

瞬态附件(Transient Attachments)

渲染过程:
  深度缓冲 → 用于 ZS 测试
  ↓
Tile 完成:
  ✅ 颜色 → 写回主内存
  ❌ 深度 → 直接丢弃(不写回!)

功耗节省:无需持久化的数据(如临时深度)可完全避免内存写入


TBR 的优势

零带宽操作

操作IMRTBR
ZS 测试读写主内存零带宽
混合(Blending)读写主内存零带宽
MSAA 4×4× 内存开销零额外带宽

其他优势

  • 瞬态内存:Vulkan 可不分配物理 RAM
  • HSR 更高效:Tile 内遮挡消除效果更好
  • 跳过未修改 Tile:无变化则不写回
  • Tile 内着色优化:空间局部性极佳

TBR 的代价

三角形成本翻倍

IMR:  顶点 ──→ [片上 FIFO] ──→ 光栅化
                    │
                始终在片上

TBR:  顶点 ──→ 几何处理 ──→ [写回主内存] ──→ [再读回] ──→ 光栅化
                              ↑               ↑
                           额外写入         额外读取

曲面细分 / 几何着色器的困境

技术IMR 表现TBR 表现
曲面细分✅ 展开数据留在 FIFO❌ 展开后写回主内存
几何着色器✅ 同上❌ 同上

这就是为何移动端很少使用曲面细分——原本为 IMR 设计的技术在 TBR 上代价极高

其他代价

  • 需分配 后变换存储缓冲(驱动管理)
  • 更粗粒度流水线:以 Render Pass 为单位
  • 更严格的 API 要求:正确使用才能获得 Render Pass 重叠

流水线行为对比

IMR:Draw Call 级流水

DC1: [Geom][Pixel]
DC2:      [Geom][Pixel]
DC3:           [Geom][Pixel]
     ← 低延迟,细粒度 →

TBR:Render Pass 级流水

RP1: [====== Geom ======][====== Pixel ======]
RP2:                     [====== Geom ======][====== Pixel ======]
     ← 高延迟,但仍可重叠 →

正确使用 API 是获得 Render Pass 重叠的前提!


为何移动端选择 TBR?

移动内容特征

特征影响
大量 2D UI / 图层像素开销为主
休闲游戏几何量少
轻量 3D(<25 万三角形/帧)三角形开销可接受
MSAA 需求TBR 零额外带宽

核心结论

手机 UI 全天候运行——像素能效决定续航!


架构谱系:并非非此即彼

◀───────────────── Architecture Spectrum ─────────────────▶
  
  │                                                       │
  │         IMR                                 TBR       │
  │  (Classic Desktop)                   (Classic Mobile) │
  │                                                       │
  │          ●──────────────────●──────────────────●      │
  │          │                  │                  │      │
  │        3DFX               Adreno             Mali     │
  │       Voodoo          (Larger Tiles)     (Strict TBR) │
  │                                                       │
  │             Modern Desktop GPUs are adopting          │
  │                Speculative Tilebased tech             │
实现特点
Mali严格 TBR,Render Pass 前后经主内存
Adreno更大 Tile,可能有混合策略
现代桌面 GPU开始借鉴 TBR 技术(如软件分块)

没有绝对优劣——设计选择取决于目标工作负载!

GPU Shader Core 架构:从 CPU 演进理解

CPU 的问题域

特征

维度CPU 特点
线程数低(16–32 线程)
程序类型长程序、控制流代码
优化目标单线程延迟

核心挑战:让少量线程跑得更快

对内存延迟极度敏感

Cache Miss 发生:
  CPU 等待 ~200 周期
  ↓
  没有其他工作可填充空闲
  ↓
  性能严重下降

CPU 的流水线与延迟隐藏

基础流水线

image.png

延迟隐藏硬件(橙色部分)

image.png

技术作用
寄存器前递中途传递结果,减少依赖等待
分支预测猜测跳转目标,提前发射指令
超标量从串行指令流中提取并行性
乱序执行大窗口(~128 条)寻找并行机会
推测执行预取可能用到的数据
大缓存减少 Cache Miss(MB 级 L1/L2)

CPU 的效率困境

颜色含义能耗
绿色实际计算单元有效功耗
橙色延迟隐藏硬件额外开销

Arm Big vs Little 核心差异:Big 核心加了更多"橙色"→ 性能提升,但 能效下降


SIMD:CPU 的效率亮点

原理

一条指令:vec4 add
  ↓
指令获取/译码:1 次
实际计算:    4 次并行
  ↓
控制开销摊薄到 1/4

局限

  • 程序必须 有 SIMD 操作 才能利用
  • 标量代码无法受益

GPU:完全不同的问题域

特征对比

维度CPUGPU
线程数16–32数十万–数百万
关注点单线程延迟整体吞吐量
程序类型复杂控制流大量相似计算

关键洞察

我们不关心一个顶点/像素的延迟,只关心最终帧缓冲的完成时间


GPU 的设计哲学

利用问题的并行性

顶点:数十万个 ──→ 每个运行 Vertex Shader
像素:数百万个 ──→ 每个运行 Fragment Shader
       ↓
   完美并行!

扩展策略

平台Shader Core 数量
移动端~20
桌面端~128

设计原则:打造最节能的单元,然后 大量复制


为何 GPU 不需要那些"橙色"硬件?

CPU 问题GPU 解法
Cache Miss 等待切换到其他线程(成千上万可选)
流水线气泡线程间隐藏延迟
分支预测失败SIMT 执行模型处理

核心思想:用 海量线程 替代 复杂硬件 来隐藏延迟

GPU Shader Core 架构:多线程延迟隐藏

从 CPU 设计出发

假设场景

  • 流水线深度:10 周期(从读取到写回)
  • 策略:让 10 个线程轮流发射
周期 0: Thread 0 → 进入流水线
周期 1: Thread 1 → 进入流水线
周期 2: Thread 2 → 进入流水线
  ...
周期 9: Thread 9 → 进入流水线
周期 10: Thread 0 → 第二条指令(此时第一条已完成!)

消除 CPU 的"橙色"硬件

CPU 技术GPU 是否需要原因
超标量发射有多个线程可选,无需从单线程找并行
乱序执行切换线程即可,无需重排单线程指令
分支预测等 10 周期后条件码已确定
结果前递上条指令结果已写入寄存器
大缓存用线程数换取延迟容忍

代价:需要存储 10 份线程状态(寄存器、PC 等)


扩展到 1000 线程:隐藏内存延迟

内存延迟问题

  • DRAM 访问:100+ 周期
  • 10 线程不够填满空闲

解决方案

1000 个线程中:
  ├─ 1 个:正在发射
  ├─ ~10 个:在流水线中执行
  └─ 其余:等待 DRAM 返回(睡眠状态)

只要每周期有 1 个线程就绪 → 完全隐藏内存延迟!

关键洞察:GPU 拥有海量线程,可以用 线程数量 换取 延迟容忍


SIMT:旋转 90° 的 SIMD

CPU SIMD 的局限

传统 SIMD:
  vec4 add → 单线程内找向量操作
  ↓
  问题:程序必须有向量指令
  现实:大多数时候 NEON (ARM架构SIMD架构扩展) 闲置

GPU 的创新:SIMT(旋转 90°)

image.png image.png

核心要求

  • 多个线程 锁步执行(Lockstep)
  • 来自 同一 Draw Call
  • 运行 相同着色器程序
  • 拥有 相同 PC(程序计数器)

术语对照

厂商/规范名称
NVIDIAWarp
AMDWavefront
VulkanSubgroup
Arm MaliWarp

Warp 宽度的优势

Warp 宽度 = 16(Mali 最新 GPU)
  ↓
控制开销摊薄到 1/16:
  - 指令获取:1 次 / 16 ops
  - 译码逻辑:1 份 / 16 ops
  - 控制状态:1 份 / 16 ops

分支发散(Divergence)问题

场景

if (condition) {
    // 路径 A
} else {
    // 路径 B
}

Warp 内行为

image.png

效率损失:分支发散导致 硬件利用率下降

最佳实践

  • 最小化 Warp 内控制流分歧
  • 同一 Draw Call 的线程尽量走相同路径

固定功能加速器

image.png

为何使用固定功能?

操作可编程 Shader固定功能
纹理过滤FP16 计算定点、更少位宽
混合通用 ALU专用硬件
变化量插值通用指令专用插值器

权衡:消耗硅面积,但 大幅节省能耗


Mali Shader Core 总览

image.png

只有蓝色部分执行着色器程序,其余为专用硬件


核心要点总结

设计决策CPUGPU
延迟隐藏硬件复杂度(乱序/预测)线程数量
向量化程序必须有 SIMD 指令硬件打包多线程
分支处理分支预测锁步 + 掩码
能效优化大缓存固定功能加速器