移动端图形与功耗 · 第 七 篇

测量与调优:怎么证明你真的优化了

2026-09-1520,314 字141 张配图本文由@溯光 出品,转载请注明出处

【移动端图形与功耗】第 7 篇 | 系列共 7 篇

站点链接:技术博客页

题图 · 一条可证伪的证据链
题图· 一条可证伪的证据链:口径、噪声、统计、门禁四段收在同一把尺子上@ 本系列绘制

系列文章前言

1.1 一句话点题:终篇要交的是什么

前六篇把机制讲完了,这一篇只回答一句:凭什么说一个优化真的生效了?

命题就一句:「优化生效了」是可证伪的测量命题,不是感觉命题。 反例要写得出:结果落进某个区间,结论就该被推翻。

1.2 原理与概念:前六篇留下的待验证清单

前六篇每篇留下一句结论;这一篇给每句配上检验它的尺子。

前六篇的结论与本篇为它配的检验量
图 7-1前六篇各留下一句结论,与本篇为它配的检验量一一对应@ 本系列绘制
篇 结论 检验量
1 物理约束 两条墙:能搬多少、能搬多久 帧时间、功耗、频率、温度
2 分块渲染(TBR) 中间物留在片上才算省 管线时间戳、片上计数器
3 带宽 字节可按「一个屏幕像素」记账 字节落点、溢出回写
4 功耗与热 可持续帧率 = 可持续功耗 ÷ 单帧能耗 稳态区间、热状态字段
5 延迟着色 一像素只读自己,预算 128 bit/像素 逐阶段时间戳、退化路径
6 现代特性 「开了」不等于「生效」 对照实验、状态回读、门禁

检验这件事一直有人在管,只是各自只管一环:Google Benchmark 文档、FOSDEM 2026 演讲材料、Android 开发者文档 · 在 CI 中做基准测试、AOSP · 性能与电源、Vulkan 规范 · 查询、Arm 学习路径 · 用 Streamline 测性能、CapFrameX · 各性能指标说明。没有一份给出「从改动到结论」的完整链条。

1.3 原理与概念:本篇的边界声明

先把不做的四件事说清楚:PC 与主机的整机评测方法学、具体游戏的帧率横评、AI 辅助调优工具、性能建模与仿真。四者各需另一套前提。

本篇的边界:范围内四件事与范围外四件事
图 7-2本篇的边界:范围内四件事与范围外四件事的对照@ 本系列绘制

三条口径写在明处:第一,「移动端」指 Mali 与 Immortalis、Adreno、PowerVR 及采用这些架构的 MediaTek 平台,不覆盖 Apple 自研 GPU 的硬件细节;第二,性能以帧时间与功耗为主,不做跑分排名,分数只是方法的例子;第三,厂商宣称值与第三方实测值分列。

1.4 原理与概念:怎么读这一篇

三条读法按目的挑:只想拿结论的,读零章末尾那张总表;要做复现的,从协议字段表起步,接第五章统计与第九章锁频 padho.ai · 性能 A/B 测试;要做判定的,看第十一章的容差窗口与统计检验 Kindatechnical · 发布门禁的统计决策。

测量方法本身就是结论的一部分

2.1 一句话点题:「我觉得快了」为什么不算数

一个优化被声称生效,需要一条完整证据链,五环:改了哪一处 → 哪个量本该跟着变 → 它变了多少 → 这个变化是否大于噪声 → 有没有第三个变量同时在动。

完整证据链的五环
图 7-3完整证据链的五环:改动、目标量、变化量、噪声边界、共变因素@ 本系列绘制
两类表述:能不能被推翻
图 7-4两类表述的对照:哪些可以被推翻,哪些连反例都提不出来@ 本系列绘制

最容易被省掉的是第四环:变化量必须跟噪声比过一次,否则「快了 3%」换台设备就能得出相反答案。

2.2 原理与概念:四个反例——同一台机器,换个条件结论就翻

说白了,测量方法本身不中立,结论就会跟着跑。

最出名的那次是三星的 GOS:把部分应用的包名做伪装后触发限频,3DMark 的 Wild Life Extreme 读数从 2618 掉到 1141 分。数字来自第三方复核,属媒体口径,精确读数与测试机型以原页为准;后续是 Geekbench 把相关机型永久移出榜单,除名范围与公告出处同样以官方公告为准。

同一台设备的两组读数(3DMark Wild Life Extreme)
图 7-5同一台设备的两组读数:常规口径 2618 与限频口径 1141@ 本系列绘制

其中:2618 分是常规口径下的读数,1141 分是限频口径下的读数,两组读数出自同一台设备。

再看 2014 年那次:华为 P7 被第三方复核认定针对基准测试做了优化,虚高最高达 28%,Futuremark 据此取消其成绩资格;UL 的《Benchmark Rules》把「针对基准测试的特定优化」写成明令禁止项 UL《Benchmark Rules》——方法握在工具手里,被测方就有动机迎合工具。

四个反例:条件一变,结论就翻
图 7-6四个反例:条件一变结论就翻,以及各自暴露的是规则问题还是口径问题@ 本系列绘制

第四个反例没有厂商,只有版本号:CapFrameX 在 1.5.3 调整了指标算法,同一份原始数据在新旧版本下读数不同 CapFrameX 1.5.3 版本说明。跑分口径与实战口径至少有三处错位:帧率对帧时间、峰值对持续、平均对尾部。

跑分口径与实战口径的三处错位
图 7-7跑分口径与实战口径的三处错位:帧率对帧时间、峰值对持续、平均对尾部@ 本系列绘制

这三处错位,Android 与 Notebookcheck 各从一侧描述过 Android 开发者文档 · 分析并优化游戏性能、Notebookcheck 测试方法。

2.3 原理与概念:四条前提

四条前提
图 7-8四条前提:同机、同场景、同热状态、同一采集口径@ 本系列绘制

先来看看四条前提。同机指同一台设备而非同型号——同型号机器的内存频率、闪存批次、固件都可能不同。同场景指同一条可复现的负载;场景一变,GPU 与 CPU 的受限关系可能从一侧翻到另一侧。同热状态是移动端最特殊的一条:设备带着上一轮的温度进场,高负载下十到十五分钟掉到约 70%(口径:媒体实测)Notebookcheck 测试方法。同一采集口径指工具、版本、算法、采样位置全部一致。

本篇的因果链
图 7-9本篇的因果链:口径决定读数,噪声决定差值能不能信,统计决定差值算不算成立,门禁决定它能不能拦住回归@ 本系列绘制

2.4 原理与概念:术语清理——帧率 / 帧时间 / 延迟 / 吞吐

帧率(frame rate)是每秒完成多少帧,帧时间(frame time)是相邻两次呈现的间隔,互为倒数——只有帧时间能直接测到,帧率永远是算出来的。延迟(latency)是输入到画面更新的时间,由帧时间、排队深度与显示链路共同决定。吞吐(throughput)是单位时间完成的工作量,可以增长而帧率不动。

四个量的关系
图 7-10四个量的关系:帧时间与帧率互为倒数,延迟多出排队与显示链路,吞吐是另一个维度@ 本系列绘制

「卡顿」在四家文档里指四件事。Android 判实际呈现时间与调度器预测是否一致,超过 700 ms 的 UI 帧叫冻结帧(frozen frame)Android 开发者文档 · 渲染时间;Apple 判实际帧寿命是否超过期望帧寿命,超出的时长叫 hitch duration(帧寿命超出量),以 ms/s 计 Apple 文档 · 理解 hitch;Qualcomm 讲 GPU 时间线与瓶颈定位 Qualcomm · Adreno 图形开发工具;Imagination 的 PVRTune 手册讲驱动与硬件计数器的读法 Imagination · PVRTune 手册。

「卡顿」在四家平台文档里的四种定义
图 7-11「卡顿」在四家平台文档里的四种定义:判定轴、门槛与输出形式@ 本系列绘制

凡说「卡顿」,先指明是哪一套口径;不指口径的卡顿率不写进结论。

2.5 实现与配置:本篇的验证方式

我们把它写成一份测量协议(measurement protocol):字段先给草稿,后面每章再补一列。

测量协议字段表(草稿)
图 7-12测量协议字段表草稿:设备与固件、工具与版本、场景与时长、样本量与重复次数、热状态、采集口径、原始数据落盘@ 本系列绘制

原始数据两条来路:逐帧分阶段时间戳 Android 开发者文档 · dumpsys、系统侧计数器与渲染阶段轨道 Perfetto 文档 · GPU 数据源。两条来路互不替代;后面的对照,靠的就是把两边的时间轴对齐。

下面开始看两条来路的取法。

adb push config.pbtx /data/misc/perfetto-configs/config.pbtx
adb shell dumpsys gfxinfo com.example.game reset
adb shell dumpsys gfxinfo com.example.game framestats > framestats.csv
adb shell perfetto --txt -c /data/misc/perfetto-configs/config.pbtx -o /data/misc/perfetto-traces/trace.perfetto-trace
adb pull /data/misc/perfetto-traces/trace.perfetto-trace

2.6 效果与量化:先给结论表

线 解决什么 代价 展开在
口径线 同一份数据换算法读数就变,先说清用哪把尺子 报告要带六项元信息 第一、二章
噪声线 移动端的一等噪声来自热与动态电压频率调节(Dynamic Voltage and Frequency Scaling, DVFS) 测量时长从秒级拉到分钟级 第五章
统计线 差异要和噪声比过才算成立 同机交替切片加配对差值 第五章
三条线各自解决什么、代价是什么
图 7-13三条线各自解决什么、代价是什么、由哪一章展开@ 本系列绘制

这张表把三条线收在一处:口径线管读数怎么来,噪声线管差值能不能信,统计线管差值算不算成立。三条线走完,「优化生效」这句话才落到实处;缺任何一条,结论都只是半句话。

噪声线的代价不是时间,是心态:三十秒的测量拉长到三十分钟,结论常常会变小。

2.7 源码与数据链接

好了,最后两条要并排看:Android 站点是文档部分 Apache-2.0、其余内容 CC BY 2.5,AOSP 站点是文档部分 Apache-2.0、其余内容 CC BY 4.0。转载配图前回到目标页面复核许可。

2.8 这一章留下的问题

零章解决了「怎么判」,还没解决「判出来是什么」:下一章先把感知量拿回来。

一、帧时间才是感知量:平均帧率为什么骗人

3.1 一句话点题:一帧从 16 ms 拉到 100 ms,平均值几乎不动

取一个十秒的窗口,60 Hz 下能装下 600 帧,每帧 16.67 ms;把其中一帧拉到 100 ms,总时长变成 10083 ms——平均帧率从 60 掉到 59.5(降幅 0.8%),平均帧时间从 16.67 ms 变成 16.81 ms;那一帧离 16.7 ms 的帧预算(frame budget)差了六倍。这六倍,在平均值里没有位置。

十秒窗口里一帧 100 ms 的尖刺
图 7-14十秒窗口里一帧 100 ms 的尖刺:平均值只动 0.8%,而那一帧是 16.7 ms 预算的六倍@ 本系列绘制

平均值把六百个样本摊薄,一个离群点就看不见了:感知量是帧间隔,不是每秒帧数。

3.2 原理与概念:帧时间的分布形态

一批帧时间不对称:一个是垂直同步(Vertical Synchronization, Vsync)在 16.7 ms 处压出的尖峰,另一个是右侧长尾。尖峰是被刷新周期挤出来的,长尾是逃出去的少数帧,两头要放在一起看;只看尖峰,会以为一切正常。

帧时间的两种形态:垂直同步在 16.7 ms 处压出的尖峰与右侧拖长的尾巴
图 7-15帧时间的两种形态:垂直同步在 16.7 ms 处压出的尖峰与右侧拖长的尾巴@ The original uploader was Jhguch at English Wikipedia.(CC BY-SA 2.5)
构造数据的直方图(1000 帧)
图 7-16构造数据的直方图:940 帧落在 16.7 ms 附近,另有 60 帧散在 20 ms 到 120 ms 之间@ 本系列绘制

换成本篇那份构造数据:一千帧里 940 帧贴在 16.7 ms,50 帧约 20 ms,9 帧约 33 ms,1 帧 120 ms。

3.3 原理与概念:平均值、中位数与分位数

先看看三个统计量:平均值是所有帧时间的算术平均,中位数(median)是排序后正中间那一个,分位数(percentile)是排序后某个位置上的值(P99 即第 99 百分位)。三者看的是同一批数据的不同位置,谁也不能替代谁。

统计量 读数 它在说什么
平均帧时间 17.1 ms 长尾摊平之后的总体印象
中位数 16.7 ms 一半的帧比它快,一半比它慢
P90 16.7 ms 九成的帧不超过它
P99 20 ms 最慢的百分之一
P99.9 33 ms 最慢的千分之一
最大值 120 ms 一次长卡顿的单点

平均值被长尾抬高了 0.4 ms,P99 与 P99.9 之间又隔了 13 ms。

箱线图把五个统计量画在同一根轴上:最小值、下四分位(Q1)、中位数(Q2)、上四分位(Q3)与最大值——3.3 表格里的六个统计量,就是这套工具在帧时间数据上的实例化;右须比左须长,正是长尾分布的箱线图长相
图 7-17箱线图把五个统计量画在同一根轴上:最小值、下四分位(Q1)、中位数(Q2)、上四分位(Q3)与最大值——3.3 表格里的六个统计量,就是这套工具在帧时间数据上的实例化;右须比左须长,正是长尾分布的箱线图长相@ Wikimedia Commons · The Twists and Turns(CC0)
平均值与中位数在同一批数据上的位置
图 7-18平均值与中位数在同一批数据上的位置:平均值被长尾抬起,中位数贴近主体@ 本系列绘制
同一批 1000 帧的六个数
图 7-19同一批 1000 帧的六个数:平均、中位、P90、P99、P99.9 与最大值@ 本系列绘制

平均 17.1 ms、中位 16.7 ms,差值全由那 10 帧贡献:只看平均值,会以为设备「均匀地慢了 2%」。

3.4 原理与概念:1% low 的两种算法

时间型(time-based)把帧按耗时从长到短累加,累到总测量时长的 1% 就停;百分位型(percentile-based)按帧数取最慢的 1%,再取平均。

时间型 1% low 的构造
图 7-20时间型 1% low 的构造:最慢帧从长到短累加,累到总时长的 1% 就停@ 本系列绘制
百分位型 1% low 的构造
图 7-21百分位型 1% low 的构造:按帧数取最慢 1%,对这一批帧取平均@ 本系列绘制

总时长 17115 ms,1% 就是 171.2 ms;时间型取最慢的 120 ms 加两个 33 ms,三帧平均得 62 ms(约 16.1 fps);百分位型取最慢的 10 帧,(9×33 + 120) ÷ 10 得 41.7 ms(约 24 fps)——同一份数据差了近 50%。

两种 1% low 算法把两个场景的排序翻了过来
图 7-22同一份构造数据的两种 1% low 算法:两个场景的排序被算法翻了过来@ 本系列绘制

排序也可能翻。场景 A:一千帧里 1 帧 400 ms,其余 16.7 ms;场景 B:20 帧 60 ms,其余 16.7 ms。时间型判 A(400 ms,约 2.5 fps)远差于 B(60 ms,约 16.7 fps);百分位型判 A(约 55 ms,约 18.2 fps)略好于 B(60 ms)。换算法结论相反。

口径迁移:CapFrameX 1.5.3 前后
图 7-23口径迁移的时间线:CapFrameX 1.5.3 前后指标算法的变化,与本篇采用的写法@ 本系列绘制

CapFrameX 在 1.5.3 调整了指标算法,同一份原始数据在新旧版本下读数不同 CapFrameX 1.5.3 版本说明。报 1% low 必须连算法一起报;第三方社区对算法取向也有整理 truebottleneck · 1% low 指南。少了这一项,两个数就不是同一个指标。

其中:171.2 ms 是总时长的 1%,62 ms 是时间型的三帧平均,41.7 ms 是百分位型的十帧平均。

3.5 原理与概念:0.1% low 的代价

下面我们进入更极端的一档。0.1% low 专盯最慢的千分之一,更接近「最难受的那一下」,代价是样本太少——两次运行差出一倍并不罕见。

0.1% low 的代价:样本太少
图 7-240.1% low 的代价:一千帧里只有一帧样本,读数随样本量剧烈摆动@ 本系列绘制

一千帧里最慢的 0.1% 只有一帧,读数等于极值本身,换一次运行就可能从 60 ms 变成 120 ms;要稳定,样本量得提一个量级——它的帧数得是 1% low 的十倍。样本不够时,这个数测的多半是运气。

箱线图的五个要素
图 7-25箱线图的五个要素:箱体给中段、须给范围、散点给离群@ 本系列绘制

分位数越高越接近最大值,也就越不稳定:报 P99.9 不写样本量,就没法被复核。

3.6 实现与配置:可比的帧率报告必带的六项

帧率报告要求六个字段齐全:工具与版本、算法、样本量、场景、热状态、原始数据。

可比的帧率报告必带的六项
图 7-26可比的帧率报告必带的六项:工具与版本、算法、样本量、场景、热状态、原始数据@ 本系列绘制
字段 为什么必须有 缺了会怎样
工具与版本 同一份数据换版本读数会变 别人复不出你的数
算法 1% low 有两套,结果可差一半 两个数其实不是同一个指标
样本量 分位数越高对样本量越敏感 P99.9 变成随机数
场景 帧时间分布随负载形态变 两组数据不可比
热状态 移动端一等噪声是热与 DVFS 峰值与持续混在一起
原始数据 第三方复核的唯一依据 结论无法被推翻,也无法被确认

落到一份报告里是这样:

{
  "tool_and_version": "<工具名 + 版本>",
  "algorithm": "time-based 1% low",
  "sample_count": 1000,
  "scenario": "十秒窗口",
  "thermal_state": "<冷启动 / 稳态>",
  "raw_data": "framestats.csv"
}

工具各有默认口径:FrameView 的定义写得细 NVIDIA FrameView 用户指南,Godot 的性能监视器只给瞬时值 Godot 文档 · 性能监视器、Godot 文档 · 性能分析总览。

三类工具的默认口径差异
图 7-27三类工具的默认口径差异:默认算法、默认时长与原始数据能不能导出@ 本系列绘制

下沉到时间戳那一层:写时间戳走 vkCmdWriteTimestamp Vulkan 参考页 · vkCmdWriteTimestamp,跨 CPU 与 GPU 时间域换算用 vkGetCalibratedTimestampsKHR Vulkan 参考页 · vkGetCalibratedTimestampsKHR,Khronos 另有示例 Khronos Vulkan-Samples · 时间戳查询示例;计数器侧是另一套读法 Android 开发者文档 · AGI 系统分析器、Vulkan 指南 · 性能分析、Android 开发者文档 · AGI 入门。它决定了你手里这个数是软件帧时间还是硬件时间。先来看看实测里的读数。

3.7 效果与量化:三种口径在同一实测上的读数差异

Arm 用 Android GPU Inspector(AGI)给 Crytek 的 Neon Noir 做调优,Mali-G76 上的单帧从 78 ms 降到约 45 ms,约 12 fps 变成约 22 fps(口径:厂商案例实测)Arm 博客 · Mali GPU 驱动与 Android GPU Inspector;按帧时间报降约 42%,按帧率涨约 75%。同一份读数,换个口径呈现,印象就完全不同。

同一改动的三种读数(Neon Noir / Mali-G76)
图 7-28同一改动的三种读数:帧时间降幅、帧率增幅与平均帧率读数,同一份数据三个百分比@ 本系列绘制

Arm 用 Streamline 分析 Cocos2d-x 的 Demo,把内存拷贝从主线程热点里挪走后,memcpy 的 CPU 占比从 54.62% 降到 9.10%,FPS 从 11.3 涨到 17.2(口径:厂商案例实测)Arm 博客 · 用 Streamline 分析 Cocos2d-x。

Cocos2d-x 案例:memcpy 占比与 FPS
图 7-29Cocos2d-x 案例的两个量:memcpy 的 CPU 占比 54.62% → 9.10% 与 FPS 11.3 → 17.2@ 本系列绘制

3.8 源码与数据链接

3.9 这一章留下的问题

帧时间的几套口径已经摆开,但「坏帧」怎么定义还没有统一答案:同一段卡顿,Android 叫 janky,Apple 叫 hitch——两个词不能互相换算。下面开始看两套平台口径的分歧。

二、掉帧不等于卡顿:两套平台口径的分歧

4.1 一句话点题:掉帧、卡顿、延迟是三件事

这三个词在平台文档里是三件事。掉帧(dropped frame)是一次主动取舍:这一帧不再按原计划呈现,直接换成更新的那一帧,Android 的 FrameTimeline 把它与卡顿分成两类 Android 开发者文档 · 卡顿检测。卡顿(janky frame)是没赶上:实际呈现时间与预测呈现时间不一致 Perfetto 文档 · FrameTimeline。延迟(latency)则可能一帧都没坏,却依然让人感到操作发黏。

Android Studio「All Frames」逐帧表:Dropped 与 Janky 被分成两类
图 7-30Android Studio「All Frames」逐帧表:Dropped 与 Janky 被分成两类@ Android Developers(CC BY 2.5)

说白了,跳过一帧未必是坏事。

4.2 原理与概念:Android 的判定口径

判定轴只有一条:实际呈现时间与调度器预测是否一致。不一致就是 janky,按断点位置细分成四类:应用没赶上截止时间(AppDeadlineMissed)、系统合成器没赶上(SurfaceFlingerDeadlineMissed)、显示硬件层出问题(DisplayHAL)、预测本身偏了(PredictionError)。

FrameTimeline 的预期呈现时间与实际呈现时间轨道
图 7-31FrameTimeline 的预期呈现时间与实际呈现时间轨道@ Perfetto 项目(Apache-2.0)

把这四类与轨道图对照,就能判断该去找应用代码、系统合成还是平台侧。

jank 原因分类明细:AppDeadlineMissed、SurfaceFlingerDeadlineMissed、DisplayHAL 与 PredictionError 四个分类
图 7-32jank 原因分类明细:AppDeadlineMissed、SurfaceFlingerDeadlineMissed、DisplayHAL 与 PredictionError 四个分类@ Android Developers(CC BY 2.5)
四类 jank 原因各自断在哪一环
图 7-33四类 jank 原因各自断在哪一环:应用线程、合成器、显示硬件与预测模型@ 本系列绘制

这段 trace 的采集配置:

{
  "buffers": [{ "size_kb": 65536 }],
  "duration_ms": 30000,
  "data_sources": [
    { "config": { "name": "android.surfaceflinger.frametimeline" } }
  ]
}

前两类在应用与系统侧可改,第三类要看平台与驱动,第四类在测量与判定本身。Android 把冻结帧的门槛定在渲染超过 700 ms 的 UI 帧 Android 开发者文档 · 渲染时间。下面我们进入 Apple 那一侧的判法。

4.3 原理与概念:Apple 的 hitch 口径

Apple 换了一根轴:它看帧寿命。期望寿命是 60 Hz 下 16.7 ms、120 Hz 下 8.3 ms,实际寿命超出期望寿命的部分就是一次 hitch,超出的时长叫 hitch duration(帧寿命超出量)Apple 文档 · 理解 hitch。轴换了,同一段画面就可能被判成两样。

hitch duration 的定义
图 7-34hitch duration 的定义:实际帧寿命超出期望帧寿命的那一段@ 本系列绘制

它的输出是每秒累计多少毫秒的卡顿时间(ms/s),门禁三档:≤10 为良好、≤25 为警告、≤50 为严重 Apple 文档 · 分析 Metal App 的性能。

hitch 门禁三档(ms/s)
图 7-35hitch 门禁三档:≤10 良好、≤25 警告、≤50 严重(ms/s)@ 本系列绘制

4.4 原理与概念:两套口径为什么不能互相换算

这两套数能互相换算吗?不能:一个比时间戳一致性,一个比帧寿命超出量;分母也一个按帧数、一个按时长。硬凑一个系数,只会把两种口径的误差叠在一起。

两套口径的判定轴对照
图 7-36两套口径的判定轴对照:一边是实际与预测的一致性,一边是帧寿命的超出量@ 本系列绘制

同一段 30 秒的 trace,前者给出「2% 的帧 janky」,后者给出「12 ms/s 的 hitch」——回答的不是同一个问题。

4.5 原理与概念:帧节奏与垂直同步

双缓冲的帧时间:须等上一帧呈现完成,约 33.4 ms
图 7-37双缓冲的帧时间:须等上一帧呈现完成,约 33.4 ms@ Khronos Group(Apache-2.0)
三缓冲的帧时间:多一级缓冲,约 16.7 ms
图 7-38三缓冲的帧时间:多一级缓冲,约 16.7 ms@ Khronos Group(Apache-2.0)

先看看这个例子。Khronos 的 swapchain_images 示例算得很直白:双缓冲(double buffering)下交换周期要等两帧,帧时间约 33.4 ms(约 30 fps);三缓冲(triple buffering)下周期缩到一帧,约 16.7 ms(约 60 fps)Khronos Vulkan-Samples · swapchain_images。

画面撕裂的成因与垂直同步的代价
图 7-39画面撕裂的成因与垂直同步的代价:等回扫换掉的是撕裂,付出的是等待@ 本系列绘制

垂直同步解决的是撕裂,代价是把呈现时刻钉在刷新周期上;还有一条路:持续投递新帧把队列塞满,帧率看着平顺,输入延迟却在上升——这叫缓冲区堆叠(BufferStuffing),Android 的帧节奏文档专门讲过 Android 开发者文档 · 帧节奏;Unity 与 Unreal 也各给过口径 Unity 文档 · 时间与帧管理、Unreal 文档 · 移动端帧节奏。

BufferStuffing 的代价
图 7-40BufferStuffing 的代价:帧率读数不变,输入延迟随排队深度增加@ 本系列绘制

4.6 实现与配置:帧预算 16.7 ms / 8.3 ms 与冻结帧 700 ms

16.7 ms 是 60 Hz 的帧预算,8.3 ms 是 120 Hz 的帧预算,由刷新率直接除出来;700 ms 则是异常线:Android 把渲染超过 700 ms 的 UI 帧归为冻结帧 Android 开发者文档 · 渲染时间。

三档阈值的刻度对照
图 7-41三档阈值的刻度对照:16.7 ms 与 8.3 ms 是帧预算,700 ms 是冻结帧异常线@ 本系列绘制

言归正传,三档阈值放一起看,就能分清「预算」和「异常线」:预算是对每一帧的要求,异常线是放弃这一帧的信号。两者混着讲,报告里就会出现拿异常线当目标的笑话。

4.7 效果与量化:同一份 trace 用两套口径判出的卡顿率

构造一段 30 秒的 trace:130 帧因错过截止时间被判为 janky,占比约 4.2%;同一段数据按 hitch 口径累计,超出帧寿命合计约 1140 ms,折合约 38 ms/s,落在 Apple 门禁的「严重」区间(口径:本篇构造数据)。

同一份 trace 的两套判定结果
图 7-42同一份 trace 的两套判定结果:Android 的 janky 帧占比 4.2% 与 Apple 的 hitch 时间占比 38 ms/s@ 本系列绘制

一个按帧数计、一个按时长计,两个数因此能同时成立;硬件侧的计数器是第三种视角 Arm 博客 · Mali Bifrost 性能计数器。两套口径各记各的账,并排摆比硬折算更有信息量。

其中:4.2% 是按帧数计的 janky 帧占比,1140 ms 是超出帧寿命的累计,38 ms/s 是按时长计的 hitch 读数。

4.8 源码与数据链接

4.9 这一章留下的问题

两套口径的原料来自不同层:帧级、系统级、计数器级、管线级;下一章问这四层各自能回答什么、会在哪里骗人。

三、四层数据源:从帧级到管线级

5.1 一句话点题:四层数据源各自能回答什么

先看看粒度这一维。按最细粒度,能拿到的东西分四层:帧级(frame level)、系统级(system level)、计数器级(counter level)与管线级(pipeline level)。

四层数据源各自能回答什么
图 7-43四层数据源分层:帧级、系统级、计数器级、管线级各自的数据来源、可回答的问题与典型工具@ 本系列绘制

四层不是「越深越好」的阶梯,而是一张「问题—工具」匹配表:帧级答「这一帧多长、卡在哪一段」,系统级答「CPU 在干什么」,计数器级答「GPU 内哪个单元在忙」,管线级答「这段管线花了多少纳秒」。

5.2 原理与概念:帧级——gfxinfo framestats

Android 上取它最省事的入口是 adb shell dumpsys gfxinfo <包名> framestats,它把最近约 120 多帧的时间戳按环形缓冲存下,每帧拆成十几个阶段、纳秒精度。dumpsys 官方文档 先来看看阶段名的读法。这些阶段名就是一份时序说明书:INTENDED_VSYNC 是系统原本安排的垂直同步(Vertical Synchronization, VSync)时刻,DRAW_START 到 SWAP_BUFFERS 是应用侧干活的那一段,相邻两项相减即常说的那几个耗时。

framestats 逐帧分阶段时间戳结构:从 INTENDED_VSYNC 到 FRAME_COMPLETED 的阶段切分与约 120 帧环形缓冲
图 7-44framestats 逐帧分阶段时间戳结构:从 INTENDED_VSYNC 到 FRAME_COMPLETED 的阶段切分与约 120 帧环形缓冲@ Android Developers(CC BY 2.5)
软件帧时间与 GPU 硬件时间的差别:应用侧缓冲区交接点并不等于 GPU 执行完成点
图 7-45软件帧时间与 GPU 硬件时间的差别:应用侧缓冲区交接点并不等于 GPU 执行完成点@ Android Developers(CC BY 2.5)

5.3 原理与概念:系统级——Perfetto

这一层的代表是 Perfetto,它最大的价值不在「能采更多数据」,而在把所有数据放进同一条时间轴:CPU 调度、GPU 提交、显示管线与频率共用一套时钟。Perfetto GPU 数据源 CPU 侧的线程级归因另有 Android Studio · CPU Profiler 可对照。

下面开始分类。GPU 数据源可归四类:gpu.counters 是厂商计数器的周期采样;gpu.renderstages 是应用提交到 GPU 的时间线,注意它是提交时间而非执行时间;FrameTimeline 把每帧拆成 Expected 与 Actual 两条线;内存类计数器记带宽与内存压力。Perfetto FrameTimeline

Perfetto 的四个 GPU 数据源:counters、renderstages、frametimeline、memory 各自的采集方式与语义
图 7-46Perfetto 的四个 GPU 数据源:counters、renderstages、frametimeline、memory 各自的采集方式与语义@ Perfetto 项目(Apache-2.0)
Perfetto 界面整体形态:多轨道并列、统一时间轴、按进程分组的 GPU 轨道
图 7-47Perfetto 界面整体形态:多轨道并列、统一时间轴、按进程分组的 GPU 轨道@ Perfetto 项目(Apache-2.0)

先整体后局部:缩到全局,得到的是各轨道在同一时间轴上的相对位置;顺着 GPU 轨道放大到逐帧,摊在眼前的才是 FrameTimeline 那两条线。前一张帮你决定往哪段放大,后一张把放大后的逐帧摊开对照。

FrameTimeline 轨道形态:Expected 与 Actual 两条时间线并列,逐帧标注是否超期
图 7-48FrameTimeline 轨道形态:Expected 与 Actual 两条时间线并列,逐帧标注是否超期@ Perfetto 项目(Apache-2.0)
# 采一段带 GPU 计数器与 FrameTimeline 的 trace
adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/gpu.pftrace <<EOF
buffers: { size_kb: 65536 fill_policy: RING_BUFFER }
data_sources: { config { name: "gpu.counters"
  gpu_counter_config { counter_period_ns: 1000000 } } }
data_sources: { config { name: "android.surfaceflinger.frametimeline" } }
duration_ms: 10000
EOF

5.4 原理与概念:计数器级——AGI 与厂商 profiler

Android 侧最好用的入口是 Android GPU Inspector(AGI),它能在真机上读 GPU 与厂商计数器——纹理带宽、纹理 L1 命中与缺失这类指标——再把逐帧 profile 与系统 trace 并排摆出来。它给的是「哪个单元在忙」,答不了「为什么慢」。AGI 入门 逐帧视图另见 AGI · 渲染通道 与 AGI · 帧剖析器。

AGI 系统分析器界面:GPU 计数器读数与逐帧 profile 并排呈现
图 7-49AGI 系统分析器界面:GPU 计数器读数与逐帧 profile 并排呈现@ Android Developers(CC BY 2.5)
Arm Streamline 的 Mali 计数器时间线
图 7-50Arm Streamline 的 Mali 计数器时间线:L2 外部读写 beats、带宽与 shader core 利用率随时间变化@ 本系列绘制

说白了,计数器名称跨代也会变,必须和芯片型号、驱动版本一起记进报告,否则过半年没人知道那串数从哪来。读法可对照 RenderDoc 文档 与 RenderDoc · 性能计数器。

Snapdragon Profiler 的三种采集模式与 Adreno 计数器
图 7-51Snapdragon Profiler 的三种采集模式(real-time / trace / snapshot)与 Adreno 计数器指标@ 本系列绘制
四家厂商 profiler 的口径对照
图 7-52AGI、Arm Streamline、Snapdragon Profiler、PVRTune 四家的平台覆盖、可读计数器与取用门槛对照@ 本系列绘制

5.5 原理与概念:管线级——时间戳与性能查询

时间戳查询的接口是 vkCmdWriteTimestamp:把当前时间写进查询池(query pool),并指定一个管线阶段作为落点。

vkCmdWriteTimestamp 的写入落点
图 7-53vkCmdWriteTimestamp 在指定管线阶段写入时间戳,并标注实现允许顺延到更晚阶段@ 本系列绘制
VK_KHR_performance_query 的枚举与采样流程
图 7-54VK_KHR_performance_query 的枚举与采样流程:查计数器集合、预留、开始、读取、释放@ 本系列绘制

$$ t_{ns} = \left((t_2 - t_1)\ \&\ \text{mask}\right) \times timestampPeriod $$

其中 t1 与 t2 是从查询池读回的原始值;mask 按 timestampValidBits 取有效位,该值可能不足 64 位并会回绕;timestampPeriod 是每个 tick 的纳秒数,由设备属性给出,各设备差别不小,换算前先查。跨设备比较之前先把这一步对齐。

时间戳的换算顺序
图 7-55timestampPeriod 与有效位掩码的换算:原始 tick 差先掩码再乘周期得纳秒,并标注回绕位置@ 本系列绘制

5.6 实现与配置:按问题选工具的决策树

规则只有一条:先选能回答问题的最浅一层,上一层给不出方向时才往下走。

按问题选工具的决策树
图 7-56按问题选工具的决策树:多快 / 为什么慢 / 应用侧还是系统侧 / 单元级 / 管线段级@ 本系列绘制
问题 → 工具 → 口径 → 局限
图 7-57问题 → 工具 → 口径 → 局限 的四列对照表,覆盖四层数据源@ 本系列绘制

好了,回到这张表。最右一列才是重点:每层能力都配着一句「它给不了什么」,记住这几处缺口比记住工具名有用。顺序不能颠倒:先把浅层能排除的方向排除掉,再决定往下走。

Godot 性能监视器界面形态:引擎侧帧耗时构成与逐项耗时排序
图 7-58Godot 性能监视器界面形态:引擎侧帧耗时构成与逐项耗时排序@ Godot Engine 文档团队(CC BY 3.0)

5.7 效果与量化:四层数据源的精度与代价对照

层 典型工具 时间精度 采集粒度 附加开销 主要局限
帧级 gfxinfo framestats / SurfaceFlinger timestats 纳秒时间戳(软件侧) 逐帧,环形缓冲约 120 帧 极低 软件帧时间;缓冲会滚掉历史
系统级 Perfetto 纳秒,统一时间轴 逐事件,含插桩 低到中,随轨道数与缓冲长度上升 数据源须提前配置;写满会丢
计数器级 AGI / Arm Streamline / Snapdragon Profiler 依工具而定,通常毫秒级采样 周期采样或逐帧 中到高,需 root 的更贵 计数器语义厂商私有,跨设备不可比
管线级 Vulkan 时间戳 / 性能查询 tick 级,由 timestampPeriod 换算 逐查询,可落在指定阶段 高,打断并行、需同步 差值只是下界;有效位可能不足 64 位

5.8 源码与数据链接

5.9 这一章留下的问题

下一章把时间戳、计数器与利用率这三处失真拆开。

四、你以为测到的,可能不是那个

6.1 一句话点题:四类精度陷阱总览

先来看看落差从哪来。按成因分,落差有四类:时间戳失真(写入允许顺延、有效位可能不足 64 位)、采样失真(计数器只是周期采样)、语义失真(频率、利用率、吞吐本是三个量)、归因失真(把工具开销算进被测对象)。

本章处理前三类,第四类留到第五章。

四类精度陷阱:成因与落点
图 7-59四类精度陷阱总览:时间戳失真、采样失真、语义失真、归因失真各自的成因与落点@ 本系列绘制

6.2 原理与概念:时间戳的三处失真

第一处是阶段顺延。规范允许把 vkCmdWriteTimestamp 的写入推迟到更晚的阶段,于是落点是一个「不早于」,相邻两个时间戳之差只是下界。Vulkan 查询章节

阶段顺延:指定阶段与实际落点
图 7-60阶段顺延:指定阶段与实际落点之间的偏移,以及它对相邻两段时间的相反影响@ 本系列绘制
timestampValidBits 不足 64 位时的回绕
图 7-61timestampValidBits 不足 64 位时的回绕:高位无意义、差值须先掩码@ 本系列绘制

第二处是有效位不足。timestampValidBits 可能小于 64,超出部分无意义,时间戳在原值上还会回绕。 好了,第三处是跨域未校准。CPU 与 GPU 各有时间域,不校准就把两边的段拼在一条轴上,段与段的相对位置就是错的,且这种错不会以异常形式出现。vkGetCalibratedTimestampsKHR 参考页

跨域校准:CPU 与 GPU 的两条时间域
图 7-62跨域校准:CPU 与 GPU 两条时间域在未校准与已校准下的对齐差异@ 本系列绘制

6.3 原理与概念:计数器是采样,不是逐 draw

硬件性能计数器(performance counter)本质是寄存器,值反映「读它的那一刻」的状态,不是一段时间的精确累加。

采集方式有两种:周期采样(periodic sampling)按固定间隔读寄存器,Perfetto 的 gpu.counters 轨道即此类;插桩采样在事件前后各读一次。

周期采样与插桩采样的区别:固定间隔读取与事件边界触发读取的时序对照
图 7-63周期采样与插桩采样的区别:固定间隔读取与事件边界触发读取的时序对照@ Perfetto 项目(Apache-2.0)
采样间隔与事件持续时间的匹配
图 7-64采样间隔与事件持续时间的匹配问题:长事件可覆盖、短事件被漏或只采到边角@ 本系列绘制

先看看下一个问题。采得到,不等于算得准。上一张说的是间隔与事件时长的匹配,决定一次采样能否覆盖目标事件;这一张往前走一步,假设读数都采到了,再问把瞬时值按周期数累加能不能还原总量。

采样值乘周期数为何不等于真实总量
图 7-65采样值乘以周期数为何不等于真实总量:瞬时量、间隔抖动与相位错配三重原因@ 本系列绘制
# 计数器两种取法二选一
data_sources { config { name: "gpu.counters"
  gpu_counter_config {
    counter_ids: 1
    counter_period_ns: 1000000      # 周期采样:固定间隔读
    # instrumented_sampling: true   # 插桩采样:事件边界读
  } } }

其中:counter_period_ns 是两次读寄存器之间的间隔,instrumented_sampling 是插桩采样的开关。

6.4 原理与概念:频率不等于利用率

频率(frequency)、利用率(utilization)、吞吐(throughput)是三个量,可以朝不同方向动。

频率是时钟跑多快;利用率是「多少比例的周期里这个单元手上有活」;吞吐是单位时间完成的工作量。频率降一半,利用率可能升上去而吞吐不变;利用率上到 90%,吞吐也可能不动——不少实现把「忙」算上了等内存的周期。

频率、利用率、吞吐三者的关系与常见误读:三者可在不同方向上同时变化
图 7-66频率、利用率、吞吐三者的关系与常见误读:三者可在不同方向上同时变化@ Perfetto 项目(Apache-2.0)
「GPU 占用 60%」的三种可能含义
图 7-67「GPU 占用 60%」的三种可能含义:时间占比、单元占用率、带宽占用率@ 本系列绘制

口径的歧义不止「占用」一处。上一张把同一个百分比拆成三种算法,说明一个词可以对应几个量;这一张换到功耗,同一个数字,取自整机与取自模块是两回事。

整机功耗口径与模块功耗口径的差
图 7-68整机功耗口径与模块功耗口径的差:屏幕、SoC 其他域与 DRAM 的归属差别@ 本系列绘制

6.5 实现与配置:三类失真的自检清单与量级

用法是:拿到一组读数先跑一遍清单,再动结论。

计数器侧与语义侧六项:采样方式是周期还是插桩、间隔多少;被观测事件的最短时长与采样间隔比是什么量级;还开着哪些采集器,会否互相打断计数;读的是哪个域的值;「利用率」的 busy 定义含哪些等待;功耗是整机口径还是模块口径。

6.6 效果与量化:同一段代码在两种读数下的归因差异

假设某帧有一段 GPU 工作。管线级时间戳测是若干毫秒;同一时刻计数器看,某单元利用率接近饱和;帧级的软件帧时间看,从 SWAP_BUFFERS 到 FRAME_COMPLETED 又是另一个数。Android 游戏性能分析

同一段 GPU 工作在三层读数下的归因
图 7-69构造示例:同一段 GPU 工作在时间戳、计数器与帧级软件时间三种读数下的差异与三种归因@ 本系列绘制

下面我们进入本章的落点:这张图要说的是读数粒度决定你能问到第几层问题,而非哪个读数更准。Vulkan 性能剖析指南

6.7 源码与数据链接

6.8 这一章留下的问题

同一台机器同一场景连跑三次,结果能差几个百分点,往往比辛苦优化出来的收益还大。

五、先说清噪声,再谈差异

7.1 一句话点题:有没有差异 vs 差异是否大于噪声

有没有差异 与 差异是否大于噪声
图 7-70有没有差异与差异是否大于噪声两个问题的层次差别:算术层面与统计层面@ 本系列绘制
统计协议七步(顺序不可换)
图 7-71统计协议总览:定义指标、估计方差、定样本量、预热稳态、交替配对、差值区间、效应量判定@ 本系列绘制

言归正传,「差异是否大于噪声」是统计问题:先说清噪声有多大,再说清差值落在噪声的哪个位置。下面的协议是我们在真机调优时反复用的,七步,顺序不能换,与工具无关。

7.2 原理与概念:样本量与方差

常用指标是变异系数(Coefficient of Variation, CV):标准差除以均值,与量纲无关。

FOSDEM 那份可靠测量的演讲给过一组实测:单轮 N=30 帧时 CV 是 6.95%,提到 N=90 后降到 4.59%;重复 M=5 轮把跨轮 CV 从 11.80% 压到 2.94%。FOSDEM 可靠测量软件性能,CV 数据的采样场景与帧率口径以其原表为准。

变异系数随样本量的下降(宽度按样本量平方根收缩)
图 7-72变异系数随样本量的下降:单轮 N=30 时 6.95%,N=90 时 4.59%,标注平方根关系@ 本系列绘制
重复次数对跨轮方差的抑制(重复 M=5)
图 7-73重复次数对跨轮方差的抑制:重复 M=5 把跨轮 CV 从 11.80% 压到 2.94%@ 本系列绘制

样本量由三个量决定:想检出的最小效应(Minimum Detectable Effect, MDE)、置信水平与刚量出的方差。

$$ n \approx \frac{2\,(z_{1-\alpha/2} + z_{1-\beta})^2\,\sigma^2}{\Delta^2} $$

其中 n 是每组所需样本量;Δ 是想检出的最小效应,也就是那个值得报告的收益;σ² 是单次读数的方差,来自第一步的实测;z(1−α/2) 与 z(1−β) 由显著性水平与统计功效决定,常取 0.05 与 0.8。

最小可检测效应、置信水平与方差三者共同决定样本量,标注反平方关系
图 7-74最小可检测效应、置信水平与方差三者共同决定样本量,标注反平方关系@ Rohanukhade(CC0)
抽样分布:样本均值的分布比单次读数更窄,宽度按样本量平方根收缩,即标准误
图 7-75抽样分布:样本均值的分布比单次读数更窄,宽度按样本量平方根收缩,即标准误@ FRuDIxAFLG(CC BY-SA 4.0)

说白了,式子的推论很直白:Δ 减半,样本量变四倍,收益越小越难证明。

7.3 原理与概念:预热与稳态

判断有没有进稳态,看三条线同时走平:帧时间序列不再下行、频率轨道进入平台、温度或热余量进入平台。三条都到位才算进入稳态,只走平其中一条就下结论,容易把预热段当稳态。

进入稳态的三条判据
图 7-76启动效应与稳态的分界:帧时间序列的下降趋势、频率平台与温度平台三条判据@ 本系列绘制
预热不足的假阳性:表面读数与实际原因
图 7-77预热不足产生的假阳性:先跑组与后跑组各自混入的漂移方向,以及被误读成收益的差值@ 本系列绘制

丢弃长度在采集之前写死,两组一致,规则也要可复现——同场景重跑,拐点应在相近位置。Android CI 基准测试

7.4 原理与概念:交替配对的 A/B

分别跑两轮的缺陷
图 7-78分别跑两轮的缺陷:单调热漂移把先跑组与后跑组分别推到漂移曲线的两端@ 本系列绘制
交替切片与配对差值的构造
图 7-79交替切片与配对差值的构造:A1 B1 A2 B2 交替采集,逐对相减得到差值序列@ 本系列绘制

正确的做法叫交替配对(paired / interleaved A/B):把总时长切成 k 个等长切片,按 A 片、B 片交替采集,每片长度记为 T;然后构造配对差值——第 i 对的差值等于同一对里 B 的读数减去 A 的读数。拿到的是一串 k 个差值,而非两组分开的读数。

配对差值为什么能抵消热漂移
图 7-80配对差值抵消热漂移的机制:慢变量在对内共享、在相减中被消掉,快噪声留在差值里@ 本系列绘制

下面开始看它为什么成立。热漂移在 T 这个尺度上是慢变量:一对里两片挨着采,热状态与后台状态几乎一致,相减即消掉;快噪声不被消掉也不被放大,留在差值序列里,靠 k 次重复平掉。

切片长度 T 有上下两条约束:下限是频率调节与热控的时间常数,T 比它短,两片取到的是同一响应过程的两个瞬间;上限是漂移显著的时间尺度,T 太长,一对之内不再共享热状态。T 只能实测,片序也要防周期化。

7.5 原理与概念:对差值做置信区间

Bootstrap 最省事也最不挑分布,流程五步:把 k 个差值当成一个样本池;从池子里有放回地随机抽 k 个;算出重抽样本的均值;把重抽与求均值重复 B 次(B 取两千到一万);把这 B 个均值排序,取第 2.5 与第 97.5 百分位,两数之间就是 95% 置信区间。

对配对差值做 bootstrap 的五步
图 7-81对配对差值做 bootstrap 的五步流程:差值样本池、有放回重抽、重复计算均值、排序、取 2.5 与 97.5 分位@ 本系列绘制
区间不含 0 才算差异成立:三种差值分布的区间与零点关系,标注成立与不成立两侧
图 7-82区间不含 0 才算差异成立:三种差值分布的区间与零点关系,标注成立与不成立两侧@ KurtSchwitters, vector Habitator terrae(CC BY-SA 3.0)

判据只有一句话:区间不含 0,才说差异成立;区间含 0,只能说这次证据不足。 含 0 时写「本次测量未能检出差异」,不写「接近显著」或「趋势上更好」,含糊表述正是下一条谬误的温床。非确定性系统的统计发布门禁

区间重叠判定为什么会出错
图 7-83为什么不能靠两个区间是否重叠来判定:独立区间比较与配对差值区间给出相反结论@ 本系列绘制
# bootstrap: B in [2000, 10000]
d = [b[i] - a[i] for i in 1..k]    # k paired differences
for b in 1..B:
    s = resample_with_replacement(d, k)
    m[b] = mean(s)
sort(m)
ci = [m[0.025 * B], m[0.975 * B]]   # 2.5 / 97.5 percentiles

其中:k 是配对差值的个数,B 是重抽的次数。

7.6 原理与概念:显著性检验怎么选

配对 t 检验(paired t-test)假设差值近似对称、无极端离群,临界值来自 t 分布。

选择规则可以压成三句:先画差值直方图,近似对称且没有极端离群就用配对 t 检验;分布明显偏斜或有个别极端值(一次垃圾回收就够造成),改用 Wilcoxon 并同时报中位数差值;两组不独立——两台设备、两个系统版本——才退回 Mann–Whitney U,并写明这不是配对设计。

一次 94 毫秒的垃圾回收停顿:与工具无关的系统停顿,会成为配对差值里的离群点
图 7-84一次 94 毫秒的垃圾回收停顿:与工具无关的系统停顿,会成为配对差值里的离群点@ Android Developers(CC BY 2.5)
三种检验方法的适用边界
图 7-85配对 t 检验与 Wilcoxon、Mann–Whitney U 的适用边界、检验对象与功效差异对照@ 本系列绘制
什么时候改用秩检验
图 7-86Mann–Whitney U 的使用场景:两组独立、存在极端离群或分布明显偏斜时改用秩检验@ 本系列绘制

言归正传,换检验方法只解决「差得可不可信」,不回答「差得值不值得」。上一张管的是分布形状把检验选错,这一张挪到另一头:显著而效应量过小,结论仍不该写。

显著不等于重要:效应量与显著性的四个象限
图 7-87显著不等于重要:效应量与显著性的四条象限,标注显著但可忽略的一角@ 本系列绘制
t 分布与正态分布尾部对比:小样本临界值更大,标注配对数为 5 时的临界值
图 7-88t 分布与正态分布尾部对比:小样本临界值更大,标注配对数为 5 时的临界值@ Kindredgrey(CC BY-SA 4.0)

这两张图要串起来读:判定「显著」用的临界值不是固定的,配对数少时这把尺子更宽;前面那张提醒,过了尺子还要看效应量够不够大。两处合读才完整。

正态假设下 95% 的临界值是 1.96;自由度小时 t 分布尾部更厚,配对数 5 时约 2.78。Commons t 分布与正态分布对比

7.7 效果与量化:六条常见谬误与各自的反例

一、用平均值代表快慢。反例:一帧从 16 毫秒拉到 100 毫秒,平均帧率几乎不动。处置:报分位数。

二、只看短测。反例:持续性能通常只有暴发的五成到六成半,Notebookcheck 高负载测试里 10 到 15 分钟掉到约七成(口径须随结果标注)。处置:测量时长与性能目标对齐。Notebookcheck 测试方法

三、顺序跑 A 再跑 B。反例即上一节的漂移偏置。处置:交替配对。

四、把一次结果当基线。处置:重复加置信区间。

五、把工具读数当硬件时间。处置:跨层交叉验证。

六、把工具自身开销算进被测对象。处置:空白对照,全关与全开各跑一遍。

六条常见谬误:反例、成因与处置
图 7-89六条常见谬误与各自的反例、成因与处置方式一览@ 本系列绘制
测量工具自身开销污染结果:采集开启与关闭两组读数之间的差值构成
图 7-90测量工具自身开销污染结果:采集开启与关闭两组读数之间的差值构成@ Android Developers(CC BY 2.5)

两张图是同一件事的两面:上一张算的是工具自身吃掉的那部分时间,这一张看的是多个工具并行时互相踩到的计数。先看开销,再看打断,归因才站得住。

多工具并行:计数互相破坏的断裂点
图 7-91多工具并行导致计数互相破坏:计数重置打断另一工具的增量,标注断裂点@ 本系列绘制

7.8 源码与数据链接

7.9 这一章留下的问题

下一章处理:哪些变量须控制住,剩下的怎么记进结果。

六、热决定你测到的是什么

8.1 一句话点题:热与频率在测量中的主导地位

同一台机器、同一段场景、同一套采集口径,只要起始温度不同,我们量到的就不是同一个东西。热状态与频率状态不落进记录,两次读数就失去可比性,这也是本章先讲热的原因。

同一段代码的两种命运
图 7-92同一段代码的两种命运:冷机短跑走出峰值曲线,热平衡之后走出持续曲线,只有后者对得上真实体验@ 本系列绘制

8.2 原理与概念:DVFS 与热节流

先看看这一节的两个概念。要说清热怎么改写读数,得先看清频率是谁在调。答案是动态电压频率调节(Dynamic Voltage and Frequency Scaling, DVFS):它让频率随负载与温度持续变动。

负载上来,系统里的调节器(governor)抬升工作点;工作点是一对参数——频率(frequency)与供电电压(voltage);瞬时功耗随之上抬,结温跟着上升;热管理介入,也就是常说的热节流(thermal throttling),再把工作点压回去。

同等工作量下只把频率减半:瞬时功耗减半、耗时翻倍,总能耗不变;只有电压也降到 0.7,能耗才落到原来的 49%。原因在于每次操作的能耗写出来是 E = C × V²,这个式子里根本没有频率。

GERİLİM Mİ, SAAT SIKLIĞI MI?
图 7-93电压与频率缩放的能耗对比(原图标注为土耳其语,数值通用):同一份工作三档设置——全速(V=1.0,f=1.0)、仅降频(V=1.0,f=0.5)、降压并降频(V=0.7,f=0.5)——能耗依次为 100%、100%、49%,底部公式正是 E = C × V²:频率不在式子里,赢家是电压@ Wikimedia Commons · Oğuz Ergin(CC BY-SA 4.0)
热节流的触发与恢复
图 7-94热节流的触发与恢复:触发点在高温侧、恢复点在稍低温度侧,两点之间形成迟滞环,同一温度可对应两个频率状态@ 本系列绘制

触发与恢复之间还有迟滞(hysteresis):恢复点通常低于触发点,同一个温度读数可能对应两个不同的频率状态。

温度对频率、功耗与帧率的三重影响
图 7-95温度对频率、功耗与帧率的三重影响:一次降频同时改写三个读数,三个读数又各自回灌回路@ 本系列绘制

三重影响叠起来是这样的:频率改写功耗(P ≈ αCV²f);温度改写漏电(leakage),漏电再回到功耗;功耗推高温度,温度拉低频率。在这个回路里,温度是状态量,不是结果。

8.3 原理与概念:峰值不等于持续

好了,峰值与持续分开看。同一台机器在冷机与热平衡两种状态下,本来就是两个工作点。 行业里把这两个工作点叫作峰值(burst)与持续(sustained):短跑测到的是峰值,热平衡之后测到的是持续。

Notebookcheck 的高负载测试口径下,多数机型在 10 到 15 分钟之间衰减到峰值的约 70%(Notebookcheck 测试方法,第三方实测值)。

10–15 分钟的衰减:持续落到峰值的约 70%
图 7-9610–15 分钟衰减到峰值约 70%:曲线先冲高、再回落、最后进入平台,落点由热平衡位置决定@ 本系列绘制

下面我们进入实测。同一口径下,红魔 9S Pro 的 60 分钟长时测试给出两个数字:平均帧率从 90 掉到 72 fps,触控延迟从 28 升到 53 ms(同源,第三方实测值)。

60 分钟长时实测:两条线的斜率并不同步
图 7-9760 分钟长时实测的两条曲线:帧率 90 → 72 fps 与触控延迟 28 → 53 ms,两条线的斜率并不同步@ 本系列绘制

常见的宣传口径是「持续性能约为暴发的 50–65%」,它没有给出可追溯的测试方法。一个数字如果说不清是怎么测的,它在协议里就是不合格的,我们的处理是标注出来、不参与判定。

持续性能占暴发比例:常见说法与带方法说明的实测
图 7-98持续性能占暴发性能比例的常见说法(50–65%):本图只画出区间并标注「口径不可追溯、不作判据」,与右侧带方法说明的第三方实测形成对照@ 本系列绘制
阶梯式衰减的形态
图 7-99阶梯式衰减的形态:频率档位离散加上迟滞,衰减不是平滑下滑而是一级一级跳,整段取平均会把台阶抹平@ 本系列绘制

8.4 实现与配置:控制变量清单

  • 屏幕亮度与自动亮度:固定亮度并关掉自动亮度。
  • 分辨率与渲染缩放:固定输出分辨率与渲染比例。
  • 刷新率与帧率上限:固定刷新率,必要时锁帧率上限。
  • 电池电量与充电状态:固定电量区间,明确是否边充边跑。
  • 后台与网络:清后台,断网或固定网络状态。
  • 环境温度与风道:记录环境温度,明确是否垫散热垫、是否有风。
控制变量六项清单
图 7-100控制变量六项清单:亮度、分辨率、刷新率、电池、后台、环境温度,逐项钉死才谈得上可比@ 本系列绘制

言归正传,说回读数来源。Android 官方的能耗性能分析器读的是设备端电源管理器(On-Device Power Monitor, ODPM)的电源轨(power rail)数据,测的是设备一级的功耗,并不针对某个应用(Android 官方能耗分析器)。

Android 官方能耗分析器的电源轨读数:Display、GPU、CPU、Memory 等子系统分轨显示
图 7-101Android 官方能耗分析器的电源轨读数:Display、GPU、CPU、Memory 等子系统分轨显示@ Android Developers(CC BY 2.5)

亮度对功耗的影响幅度只有「不同亮度下差异从 42% 缩到 9%」这类没有测试方法的说法,只能算现象举例。

环境温度与机身温度的记录方式
图 7-102环境温度与机身温度的记录方式:环境温度用独立温度计、机身温度取背面固定点,两者的差值才反映散热能力@ 本系列绘制

8.5 实现与配置:把热状态记进结果

先来看看落盘字段。读数只有配上环境,才是可比的。

字段 取值方式 为什么必须留
设备型号与个体编号 机型 + 序列号后四位 区分同型号不同个体
系统与驱动版本 系统 build + GPU 驱动版本 驱动版本会改热策略
环境温度 独立温度计 散热边界条件
起始机身温度 背面固定点 决定起跑线
采样时刻温度 CPU / GPU / 电池三处 判定是否已进入稳态
采样时刻频率 CPU / GPU 频点 判定是否已被降档
热余量读数 getThermalHeadroom 平台给出的统一口径
预热时长与跑测时长 秒 决定读数落在哪一段
是否锁频、是否充电 布尔 影响可比性

表里的字段分成四组:设备与环境划出边界,采样时刻的温度与频率说明读数落在哪一段,热余量给出平台刻度,跑测条件决定可比性。缺一组,读数就只能自己跟自己比。

adb shell dumpsys thermalservice
adb shell cat /sys/class/thermal/thermal_zone0/temp
adb shell cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
adb shell dumpsys battery

其中:thermal_zone0/temp 是机身温度采样,scaling_cur_freq 是当前频点。

热状态落盘的四组字段
图 7-103热状态落盘的四组字段:设备与环境、采样时刻读数、热余量、跑测条件@ 本系列绘制

8.6 效果与量化:同机两次测量的热漂移量级

时间窗口 读数漂移量级 依据
前 10 分钟以内 个位百分比 尚未进入首次降档,漂移主要来自调节器抖动
10–15 分钟 约 30% 衰减到峰值约 70%(第三方实测,换算)
60 分钟长时 帧率约 20%、触控延迟近 90% 90 → 72 fps、28 → 53 ms(第三方实测,换算)

(表内为量级参照,由前文第三方实测的衰减幅度换算而来,不是我们自己的实测;凡要用于具体结论,须在目标机型上自行复测。)

「我们优化了 15%」这句话,如果不写清测了多久、起始温度多少,大家没法判断它是优化还是热漂移。

8.7 源码与数据链接

Arm、Qualcomm、Imagination 等厂商的 profiler 界面与实测曲线不可转载,本章用到的温度-频率关系图与漂移曲线都由本系列自绘,数据取自上列公开来源。

8.8 这一章留下的问题

热把「测多久」变成了协议的一部分,但它只回答了读数会变,没回答该以哪一段时间为准。

七、先定位,再优化:判定与对照实验

9.1 一句话点题:定位流程五步

优化之前先证明瓶颈在哪,证明不了就别动代码。 五步是固定的:定现象 → 采数据 → 判阈值 → 做对照 → 分场景结论。

这五步的顺序不能颠倒:先有现象才有数据,先有数据才谈阈值,先有阈值才谈对照,最后才把结论按场景分开陈述;跳过任何一步,后面的判断都会失去依据。

定位流程五步
图 7-104定位流程五步:定现象 → 采数据 → 判阈值 → 做对照 → 分场景结论,每一步各有对应的工具与判据@ 本系列绘制

9.2 原理与概念:判定阈值

下面我们进入阈值。Android 官方给出一组分流阈值(Android 游戏优化总览、Android 游戏性能优化,官方口径):

  • GPU 受限:渲染硬件接口(Rendering Hardware Interface, RHI)线程耗时持续 > 30 ms;GPU 完成时间 > 25 ms;GPU 利用率 > 90%。
  • CPU 受限:CPU 利用率 > 15%,且 GPU 利用率与帧率同时偏低。
Android 官方 Unreal Insight 截图:GameThread / RenderThread / RHI 三条线程的耗时分布,线程级耗时的第一手读数
图 7-105Android 官方 Unreal Insight 截图:GameThread / RenderThread / RHI 三条线程的耗时分布,线程级耗时的第一手读数@ Android Developers(CC BY 2.5)

三条线程里 RHI 那一栏最能说明问题,它代表提交之后到 GPU 完成的等待,这就是阈值的定义式依据。这一栏持续超线,瓶颈就已经落在 GPU 侧;反过来,三条线程的耗时都不高而帧率上不去,就该往 CPU 侧去找。

Android 官方截图:GPU 完成块的等待时间与三条阈值并列,RHI > 30 ms / GPU 完成 > 25 ms / 利用率 > 90%
图 7-106Android 官方截图:GPU 完成块的等待时间与三条阈值并列,RHI > 30 ms / GPU 完成 > 25 ms / 利用率 > 90%@ Android Developers(CC BY 2.5)
Android 官方截图:CPU 利用率 > 15% 且 GPU 与帧率双低 → 判定 CPU 受限
图 7-107Android 官方截图:CPU 利用率 > 15% 且 GPU 与帧率双低 → 判定 CPU 受限@ Android Developers(CC BY 2.5)

两条都不满足的中间地带最需要小心。

9.3 实现与配置:对照实验法

好了,看对照实验怎么设计。这一步只能靠对照实验,核心是一次只改一个变量,而且改的必须是能放大瓶颈的那个变量。改的变量若没落在瓶颈上,读数不动只能说明这次没碰到真正的成本项。

对照 自变量 预期读数变化 能排除什么
分辨率减半 渲染像素量 帧率大涨 → GPU 侧 瓶颈不在像素侧
关后处理 Bloom / SSAO / 色调映射开关 帧时间增量落在这几段 后处理链的成本占比
减 AI 与物理 实体与碰撞数量 帧率跟随逻辑负载 CPU 侧的逻辑成本
换渲染路径 前向 / 延迟 大改动,仅在上三步同向时用 路径级取舍
分辨率减半对照:读数怎么走,瓶颈就在哪一侧
图 7-108分辨率减半对照:像素量减半后帧率大涨即 GPU 受限,帧率几乎不动则瓶颈不在像素侧@ 本系列绘制
四组对照实验:自变量、预期读数变化与能排除的假设
图 7-109四组对照实验表:自变量、预期读数变化与能排除的假设,按成本从低到高排序@ 本系列绘制

引擎侧的帧时间口径可以直接对表读(Unity 帧时间管理、UE 移动端帧节奏)。

9.4 效果与量化:分场景量化

固定起点与终点、固定镜头路径、固定敌我数量与画质档位。

把一局游戏切成可比的场景段
图 7-110把一局游戏切成可比的场景段:固定起止点与镜头路径,按段上报而不是整局平均@ 本系列绘制

先来看看分场景怎么切。分场景读数有三条可比性前提:段的边界由可重放的脚本定义(不是人工掐表);每段的样本量足够;段的划分在 A/B 两侧完全一致。用 Perfetto 的 FrameTimeline 判段,比用帧率阈值判段更贴近感知(Perfetto FrameTimeline)。

分场景读数的三条可比性前提
图 7-111分场景读数的三条可比性前提:脚本化边界、样本量足够、A/B 分段一致@ 本系列绘制

工具侧的选择按问题走:要逐帧分阶段时间戳用 gfxinfo framestats(dumpsys 用法);要看系统级时间线用 Perfetto(Perfetto GPU 数据源);要看计数器用 AGI(AGI 入门、AGI 系统分析器)。经验值是三到六段。

adb shell dumpsys gfxinfo com.example.app framestats
adb shell dumpsys gfxinfo com.example.app
adb shell dumpsys SurfaceFlinger --list
adb shell perfetto -o /data/misc/perfetto-traces/trace_file.perfetto-trace -t 20s sched freq gfx view

其中:framestats 是逐帧的分阶段时间戳,Perfetto 那条是系统级时间线。

9.5 源码与数据链接

本章三张截图取自 Android 官方文档(站点其他内容 CC BY 2.5),署名与许可见文末图片版权表。

9.6 这一章留下的问题

定位把瓶颈分成了两类,对照实验把责任落到了具体改动上。

八、目标是稳态而不是峰值:长时测量

10.1 一句话点题:性能目标与热限制的关系

产品要守的是一段时长里的下限,不是峰值。 帧预算封住上限——60 Hz 下一帧 16.7 ms,120 Hz 下一帧 8.3 ms,超过就是 jank(Android 渲染时间,官方口径)。

说白了,两个预算各管一头。峰值与下限是两件事:帧预算管一帧之内的上限,热预算管一段时间里的下限,可持续区间就落在两者之间;测量要回答的,是这段区间有多宽、何时开始收窄。

性能目标与热限制的关系
图 7-112性能目标与热限制的关系:帧预算封住上限、热预算封住下限,可持续区间就在两者之间@ 本系列绘制

10.2 原理与概念:热余量 API

平台给的是 getThermalHeadroom,也就是热余量(thermal headroom):取值从 0.0 到 1.0,1.0 表示还有余量,越接近 0 越接近热极限(Android ADPF 热管理,官方口径)。它属于 Android 动态性能框架(Android Dynamic Performance Framework, ADPF):各家温度传感器的位置、刻度、滤波都不一样,「机身 42 摄氏度」在两台机器上不是同一件事。

Android 官方:接入 ADPF 热 API 之后,热余量被画成一条随时间变化的轨道
图 7-113Android 官方:接入 ADPF 热 API 之后,热余量被画成一条随时间变化的轨道@ Android Developers(CC BY 2.5)

下面开始看这条 API 的用法。用它有一条硬限制:调用频率不得高于每 10 秒一次,超过就返回 NaN(官方原文)。

调用频率限制与 NaN 的成因
图 7-114调用频率限制与 NaN 的成因:余量按长窗口滤波,10 秒以内的重复查询得不到有效值@ 本系列绘制

余量与温度并不同向:温度是瞬时量,余量是滤波后的预测值。

PowerManager pm = getSystemService(PowerManager.class);
float headroom = pm.getThermalHeadroom(10);
int status = pm.getCurrentThermalStatus();
Log.d(TAG, "headroom=" + headroom + " status=" + status);

其中:getThermalHeadroom 是热余量,10 是预报窗口的秒数。

10.3 原理与概念:持续性能模式

平台给出的机制是持续性能模式(sustained performance mode),自 Android 7.0 起在系统侧提供支持(AOSP 电源与性能),代价是峰值被削掉一截。削掉的幅度不写进文档,只能靠长时测试量出来,得自己跑一遍。

Android 官方:接入之前的形态,应用看不到热余量,只能被动接受平台降档
图 7-115Android 官方:接入之前的形态,应用看不到热余量,只能被动接受平台降档@ Android Developers(CC BY 2.5)

另一条路是锁频(frequency locking):好处是去掉调节器抖动;坏处是它与平台的热策略直接冲突。

做法 抖动 峰值 与平台的关系
不干预 大 完整 完全交给平台
锁频 小 完整 与热策略冲突,可能被覆盖
持续性能模式 小 被削 与平台协商
三种长跑做法:抖动、峰值与平台关系的取舍
图 7-116三种长跑做法对照:不干预、锁频、持续性能模式,各自在抖动、峰值与平台关系上的取舍@ 本系列绘制

10.4 实现与配置:长时测量的设计

档位 时长 用途 不能用来做什么
冒烟档 30 秒 确认链路通、口径对、数据能落盘 不做任何性能结论
中长档 5 分钟 跨过多数机型的首次降档 代表不了稳态
长时档 30 分钟 观察稳态与二次降档 不适合放进每次提交的门禁
三档时长的用途对照
图 7-117三档时长的用途对照:冒烟档看链路、中长档跨首次降档、长时档看稳态与二次降档@ 本系列绘制

采样与掉线处理有三条规矩:采样间隔固定;掉线只做标记、不做插值;对齐用时间戳、不用序号。

长时测量的采样与掉线处理
图 7-118长时测量的采样与掉线处理:固定间隔采样、掉线标记不插值、按时间戳对齐@ 本系列绘制

时长之外还有一条纪律:同一档位内的跑测次数要固定。30 分钟档跑两次与跑五次,中位数本身的稳定性差别很大。跑测次数不固定,这条纪律就落不了地。

10.5 效果与量化:三档时长下的结论差异

好了,把三档差异摆出来。下面这张表是机制性的预期,依据来自前面已经引用过的第三方实测(10–15 分钟衰减到约 70%、60 分钟帧率 90 → 72 fps),不是我们自己的跑测数字。

档位 假设的 A/B 差异 能不能下结论
30 秒(冷机) 差异落在噪声内 不能,冒烟档只验链路
5 分钟 差异小于噪声量级 不能,最多算观察
30 分钟 差异大于漂移量级 可以,但须附热状态字段

10.6 源码与数据链接

本章两张热 API 截图取自 Android 官方文档(站点其他内容 CC BY 2.5),署名与许可见文末图片版权表;其余曲线与结构图由本系列自绘。

第三方实测的衰减幅度只用于说明量级,没有参与任何判定。

九、让回归自己冒出来:把测量接进 CI

11.1 一句话点题:人工比对与门禁判定的差别

人工比对与门禁判定的差别不在精度,在可重复性。这套判定的原理是:让阈值与统计替人做判断。门禁判定把这些都交给同一套阈值与统计,跑一次和跑一百次给出同一个答案。同一份曲线换个人看,结论就可能不同。

人工比对与门禁判定
图 7-119人工比对与门禁判定:前者靠人看曲线、不可重复;后者按阈值与统计判定,每次提交都能跑@ 本系列绘制

11.2 实现与配置:真实设备与锁频

下面我们进入设备。第一件事是别用模拟器:模拟器没有真实的 GPU 驱动栈、没有 DVFS、也没有热行为(Android CI 基准测试,官方口径)。

为什么不能用模拟器
图 7-120为什么不能用模拟器:缺 GPU 驱动栈、缺 DVFS、缺热行为,三项都直接改写帧时间@ 本系列绘制

第二件事是靠锁频压噪声:把频率钉住之后,同一个样本的离散度会明显收窄。

锁频对测量稳定性的作用
图 7-121锁频对测量稳定性的作用:同一场景下锁频前后的样本离散度对比,均值可能相同而方差不同@ 本系列绘制

第三件事是设备池与热等待:上一轮跑完的机器是热的,如果不等冷却就开下一轮,第 N 轮的起跑温度就是第 N−1 轮的残留。热等待的时长也要写进跑测条件。

CI 设备池与热等待的三层结构
图 7-122CI 设备池与热等待的三层结构:设备池、冷却队列、跑测窗口@ 本系列绘制

设备池的规模也有下限:一台机器跑不出个体差,三台以上才有意义。

11.3 实现与配置:相对基线的容差判定

回归判定的对象是相对基线的变化量。

基线文件与中位数采样的比较方式
图 7-123基线文件与中位数采样的比较方式:指纹校验 → 场景与口径对齐 → 中位数比较 → 容差判定@ 本系列绘制

选中位数是有理由的:均值会被长尾拖走,而移动端的帧时间天生有长尾。容差窗口之外还要加抖动抑制,三条一起用:扔掉每个跑测窗口的首轮样本(预热期);取中位数;连续 N 轮同向才算回归。

容差窗口的设定与抖动抑制
图 7-124容差窗口的设定与抖动抑制:去首轮、取中位数、连续 N 轮同向才算回归@ 本系列绘制

先看看这个窗口怎么定。容差窗口的宽度应当由噪声决定:先在同一版本上连跑 N 轮,再把窗口设在分布的 95% 分位附近。

11.4 实现与配置:用统计检验而非肉眼

判定链是固定的四步:先构造配对差值(paired difference)(同一台机器上交替跑 A 与 B,取每对的差),再对差值做置信区间(confidence interval),然后看区间有没有跨过 0,最后看效应量(effect size)。

两次基准比较的判定链
图 7-125两次基准比较的判定链:配对差值 → 差值置信区间 → 是否含 0 → 再看效应量@ 本系列绘制
paired = [a - b for a, b in zip(A, B)]
lo, hi = bootstrap_ci(paired, 0.95)
if lo <= 0 <= hi:
    return "no-diff"
return "regression" if abs(effect_size(paired)) >= gate else "significant-only"

其中:配对差值是同一台机器上 A 与 B 的逐对之差,95% 是区间分位。

第二步是整条链的关键:区间含 0 就不能宣称有差异。

显著不等于值得改
图 7-126显著不等于值得改:显著性回答有没有差异,效应量回答差异有多大,后者才是排期依据@ 本系列绘制

配对差值这一步有个前提:A 与 B 必须交替跑,不能先跑完 A 再跑 B。

显著不等于值得改。样本量足够大时,0.5% 的差异也能被检出为显著,但为它排一次重构未必划算。门禁可以做两档:显著即报警,效应量超过人工设定的门槛才算回归。

11.5 效果与量化:CI 上的六类干扰源

CI 上最常出现的干扰源有六类:设备个体差、系统与驱动更新、后台任务与温控策略、设备热历史、采集工具自身开销、以及把一次跑分当基线。

CI 上的六类干扰源
图 7-127CI 上的六类干扰源:个体差、版本更新、后台与温控、热历史、工具开销、单次基线@ 本系列绘制

好了,这一类单独说。第五类值得单独展开:开发者选项里的「GPU 呈现模式」本身也在读 gfxinfo,所以它算一个工具(Android 渲染时间、dumpsys 用法)。测量工具的开销污染结果这件事,在方法学上有专门的经验总结可参考(FOSDEM 测量方法演讲、Google Benchmark 工具说明)。

把一次跑分当基线的后果
图 7-128把一次跑分当基线的后果:那一次的起始温度被焊进基线,之后所有判定都被整体平移@ 本系列绘制

第六类的后果最隐蔽:一次跑分里含着当时的起始热状态,把它当基线,等于把那一刻的温度固化成了判据。发布门禁的统计判据怎么设,有专门的工程讨论可查(不可确定系统的统计决策、性能 A/B 方法页)。

11.6 源码与数据链接

先来看看怎么落地。落地顺序建议是「先采集、再判定、后门禁」:第一步只把数据跑出来并落盘,不设任何阈值;第二步在历史数据上离线试算容差与轮数;第三步才把判定接进提交流程。

11.7 这一章留下的问题

六个信号入了门禁,回归能从流水线里自己冒出来了,但门禁只能守住我们量得到的东西。

十、把机制收束成可验证:七篇的账

12.1 一句话点题:哪些结论已被验证、哪些只是解释

说白了,这一章做的是对账。前六篇讲了六层机制,这一章把它们分成两堆:哪些结论已经被独立证据验证过,哪些目前只是解释。 两栏的界限,决定了哪些数字能直接引用、哪些不能。

七篇结论的总账:已可复核与仅为解释
图 7-129七篇结论的总账:分「已可复核」与「仅为解释」两栏,逐条标注来源类型@ 本系列绘制

12.2 全量回收:前六篇的量化结论

结论 数值 来源类型 可否独立复核
帧预算 60 Hz → 16.7 ms;120 Hz → 8.3 ms 平台官方口径(Android 渲染时间) 可(定义式,无争议)
延迟着色经典四段账 写 4 + 读 ≥4 + 混合 ≥2 ≈ 10 BU 厂商口径(Khronos 的移动端最佳实践) 部分(须先复现其 BU 定义)
片上路径下的账 约 1 BU 厂商口径(同上一行来源) 部分(可用带宽计数器反推)
内存能耗量级 RAM 约 100 pJ 对片上缓存约 10 pJ 厂商口径(来源见参考资料第三组) 部分(只能靠计数器间接验证)
持续性能占暴发的比例 50–65% 无测试方法可追溯,不给出来源 不可(本系列不作判据)
长时帧率与触控延迟 90 → 72 fps;28 → 53 ms 第三方实测(Notebookcheck 测试方法) 可(同一测试方法可复现)
带宽与功耗的量化结论回收
图 7-130带宽与功耗的量化结论回收:逐条标注来源类型与可否独立复核,厂商口径与第三方实测分列@ 本系列绘制
结论 数值 来源类型 可否独立复核
隐藏面消除的红利 应用提交 4.7 fragments/像素 → 实际着色 1.2(少 75%) 厂商实测(PowerVR 的 GDC 2020 材料) 可(同型号硬件上可重复)
分块带来的削减 tile 数与带宽各约 −55% 厂商口径(来源见参考资料第三组) 部分
片上预算硬线 每像素 128 bit(Mali-G72 起 256 bit) 引擎与厂商官方口径(Khronos 的移动端最佳实践) 可(引擎文档里写死)
合并失败的指纹 片段任务约翻倍(56/s → 113/s) 官方样例(Khronos 的子通道样例,见本系列第五篇) 可(样例可下载复现)
延迟着色与分块的量化结论回收
图 7-131延迟着色与分块的量化结论回收:厂商实测、厂商口径与官方样例三类来源并列@ 本系列绘制
结论 数值 来源类型 可否独立复核
双缓冲对三缓冲 33.4 ms(30 fps)→ 16.7 ms(60 fps) 官方样例(Vulkan-Samples 的 swapchain_images) 可(仓库可下载、步骤公开)
移动端光栅优化(Crytek Neon Noir on Mali-G76) 单帧 78 ms(12 fps)→ 减 33 ms → +43% → 22 fps 厂商演示(Arm 的 Mali 驱动与 AGI 材料) 部分(演示场景非公开)
memcpy 占比与帧率(Cocos2d-x + Streamline) 54.62% → 9.10%;11.3 → 17.2 fps 厂商案例(Arm 的 Cocos2d-x 案例) 部分(可在自己工程上自测同类改动)
GPU 受限判定阈值 RHI > 30 ms;GPU 完成 > 25 ms;利用率 > 90% 平台官方口径(Android 游戏性能优化) 可
CPU 受限判定阈值 CPU 利用率 > 15%,且 GPU 与帧率双低 平台官方口径(同上一行来源) 可
官方样例、厂商演示与厂商案例的可复核程度
图 7-132VRS、网格着色与光追的量化结论回收:官方样例、厂商演示、厂商案例三类来源的可复核程度对照@ 本系列绘制

还有两条属于「测量口径」而非性能结论,一并回收。

先看看这两条口径。一条是 Apple 的卡顿门禁:以 ms/s 计,≤10 良好 / ≤25 警告 / ≤50 严重(Apple 官方口径)。另一条是两个跑分判例:三星 GOS 使 Wild Life Extreme 从 2618 掉到 1141,华为 P7 因针对基准优化虚高最高 28%;行业处置是永久除名与取消资格,条款依据写在 UL Benchmark Rules 里。

第三方可复核来源与厂商口径的分界
图 7-133第三方可复核来源与厂商口径的分界:哪些结论可以直接引用,哪些必须标注「厂商口径」或自行复测@ 本系列绘制
{
  "gpu_limited": { "rhi_ms": 30, "gpu_finish_ms": 25, "util_pct": 90 },
  "cpu_limited": { "cpu_util_pct": 15, "gpu_low_and_fps_low": true }
}

其中:RHI 是渲染硬件接口线程的耗时,25 是 GPU 完成时间的阈值。

前六篇的结论也能逐条挂到规范与厂商页上:

前六篇结论 来源 可复核程度
网格着色 VK_EXT_mesh_shader 提案、Vulkan 规范 · VK_EXT_mesh_shader、interplay of light · meshlets、krupitskas · 现代剔除、tmarrec · meshlet 剔除、linebender · 网格着色 部分
VRS 与降低精度 NVIDIA VRS、Godot VRS、Wikipedia VRS、Android · Vulkan 降低精度 部分
移动光追的遍历 Vulkan 规范 · 光线遍历 可
动态渲染与片上路径 VK_KHR_dynamic_rendering、VK_KHR_dynamic_rendering_local_read 可
子通道合并失败的指纹 VkSubpassMergeStatusEXT、Vulkan 规范 · 渲染通道 可
移动端光栅优化 Vulkan 规范 · 图元光栅化 可
内存能耗量级 PowerVR · 硬件架构概览 部分
分块带来的削减 Qualcomm · 移动端最佳实践、Arm 开发者文档 101897 部分
引擎侧口径 Unity · 时间与帧管理、Unreal · 移动端渲染特性 可

12.3 效果与量化:一份可复现清单

五条最不可省的测量纪律
图 7-134五条最不可省的测量纪律:四条前提、先测噪声、配对差值、热状态落盘、基线门禁@ 本系列绘制

下面开始把协议收成清单。配上第五章的样本量结论(单轮 N=30 时变异系数 6.95%,N=90 降到 4.59%;重复 M=5 把跨轮变异系数从 11.80% 压到 2.94%),这五条就构成完整协议。

字段的价值在于每一项都能落到一次可复跑的采集上。

组 字段
设备 机型、个体编号、系统 build、GPU 驱动版本
场景 场景脚本、镜头路径、画质档位、分辨率与渲染缩放
口径 帧时间来源、统计量、样本量、重复次数、工具与版本
热 环境温度、起始机身温度、采样时刻温度、频率、热余量、跑测时长
判定 基线指纹、容差窗口、判定方法、连续同向轮数
完整测量协议的字段表
图 7-135完整测量协议的字段表:设备、场景、口径、热、判定五组字段,可直接做模板@ 本系列绘制
从问题到结论的判定树
图 7-136从问题到结论的判定树:口径 → 数据层 → 归因 → 对照实验 → 统计判定,五级逐步收窄@ 本系列绘制

12.4 原理与概念:证明不了的部分

言归正传,说回边界。第一类证明不了的是因果:混杂因素(confounder)与中介变量(mediator)在图上长得很像,但两者的含义完全不同:前者必须被控制,后者恰恰是机制本身。现有工具能指到位置,却替不了这一步。

混杂因素与中介变量:位置不同,处置不同
图 7-137混杂因素与中介变量:一个在因果链之外必须被控制,一个在链之中就是机制本身@ 本系列绘制
现有工具的证明边界
图 7-138现有工具的证明边界:因果只能到相关、计数器只能到采样、时间戳只能到实现允许的位置@ 本系列绘制

十一、结语与系列收尾

13.1 效果与量化:系列七篇的索引与阅读路径

篇 主题 一句话价值
一 两台不同的机器:移动 GPU 的物理约束 先说清约束,后面才谈取舍
二 分块渲染:binning、片上内存与它的代价 分块的收益与三笔代价
三 带宽:移动端最硬的那道墙 四类手段的收益排序与验证
四 功耗与热:帧率为什么会掉下去 可持续帧率与热闭环
五 延迟着色在移动端:能做什么、不能做什么 一拆 pass 就前功尽弃
六 现代特性落地:VRS、网格着色与移动光追 开了不等于生效
七 测量与调优:怎么证明你真的优化了 把结论变成可复核的证据

七篇的顺序不是随意的:先立约束、再谈手段、最后讲证法,前面的结论在后面的篇里被当成条件使用。想解决某个具体问题的读者,可以直接跳到对应的一篇。

顺着这份索引回看整条线:第 1 篇立约束、第 2 篇谈机制、第 3、4 篇分别算字节账与焦耳账、第 5 篇拿去审延迟着色、第 6 篇落地新特性,最后这篇把每篇的结论都收到「可复核的证据」上——篇篇独立可翻,但连起来才是一套完整的证法。

13.2 实现与配置:源码与数据链接

全系列资源的五层结构
图 7-139全系列资源的五层结构:规范层、平台层、厂商层、工具层、素材层,下层定义上层的边界@ 本系列绘制

Android 文档站点的文档内容是 Apache-2.0,但站点其他内容(含配图)是 CC BY 2.5,不是常见的 CC BY 4.0;AOSP 站点才是文档 Apache-2.0、其他内容 CC BY 4.0。

素材检索留痕(Commons):Commons 与本主题(移动端 GPU 的测量与调优)重合度极低,下列类目页与文件页记录的是检索路径,不都是本篇的引用来源。

类型 页面
Category Frame rate
Category Benchmarking
Category Computer performance
Category Metrics
Category Statistics
Category Statistical charts
Category Data visualization
Category Box plots
Category Normal distribution
Category Confounding
Category Computer cooling
Category Display technology
Category Graphics processing units
Category Analysis of variance
Category Measurement
Category Metrology
Category Video game development
Category Histograms
File Boxplot vs PDF.png
File Beispielboxplot.png
File 2 box plots - fr.jpg
File Vsync diagram.svg
File Normal distribution 50% CI illustration.svg
File Faktor-mediator-confound.png
File Confounding.svg
File Triple buffering.png
File Double buffering.png
File Comparison double triple buffering.svg
File Screen tearing on DOSV.png
File AllanDeviation.svg
File Confidence interval by Standard deviation.svg
File Confidence intervall normal dist.svg
File P-value in statistical significance testing.svg
File Repeated Measures ANOVA Example.png
File Main-sources-of-Type-B-uncertainty.svg
File Banakuntza normala histograma 01.png
File Videosignal vsync.jpg
File QAS Sample Histogram Chart of Gas Expenses.png

13.3 效果与量化:写给下一个系列

三个还没答完的问题,留给下一个系列:

一是跨设备的下限。本篇协议都是在单机上立的,换一台机器、换一颗芯片,同一条结论还能不能守住,需要一套跨机型的方法。

二是自动化的边界。回归能自动跑,但「为什么慢了」仍然要人来看;能自动化到哪一步、哪一步必须留给人,还没有答案。

三是能耗与画质的联合判定。本篇把性能和功耗分开量,但产品要的是两者的比值——这个比值怎么定义、怎么进门禁,是下一个系列的第一个问题。

参考资料

本篇正文在每处论点就近给出了出处;以下是完整来源清单,分七组排列,涉及数字与条款的条目都能回溯到原文,便于逐条查证与延伸阅读。

一、平台与官方口径(Android / AOSP / Perfetto / Apple)

  1. https://developer.android.com/tools/dumpsys
  2. https://developer.android.com/agi/start
  3. https://developer.android.com/agi/sys-trace/system-profiler
  4. https://developer.android.com/agi/frame-trace/renderpasses
  5. https://developer.android.com/agi/frame-trace/frame-profiler
  6. https://developer.android.com/studio/profile/jank-detection
  7. https://developer.android.com/studio/profile/energy-profiler
  8. https://developer.android.com/studio/profile/cpu-profiler
  9. https://developer.android.com/games/optimize
  10. https://developer.android.com/games/optimize/gameperformance
  11. https://developer.android.com/games/optimize/vulkan-reduced-precision
  12. https://developer.android.com/games/optimize/adpf/thermal
  13. https://developer.android.com/games/sdk/frame-pacing
  14. https://developer.android.com/topic/performance/benchmarking/benchmarking-in-ci
  15. https://developer.android.com/topic/performance/vitals/render
  16. https://developer.android.com/license
  17. https://source.android.com/license
  18. https://source.android.com/docs/core/power/performance
  19. https://perfetto.dev/docs/data-sources/gpu
  20. https://perfetto.dev/docs/data-sources/frametimeline
  21. https://developer.apple.com/documentation/xcode/understanding-hitches-in-your-app
  22. https://developer.apple.com/documentation/xcode/analyzing-the-performance-of-your-metal-app/

二、Vulkan 与 Khronos:规范、参考页与官方样例

  1. https://docs.vulkan.org/guide/latest/profiling.html
  2. https://docs.vulkan.org/refpages/latest/refpages/source/vkCmdWriteTimestamp.html
  3. https://docs.vulkan.org/refpages/latest/refpages/source/vkGetCalibratedTimestampsKHR.html
  4. https://docs.vulkan.org/spec/latest/chapters/queries.html
  5. https://docs.vulkan.org/samples/latest/samples/api/timestamp_queries/README.html
  6. https://docs.vulkan.org/spec/latest/chapters/primsrast.html
  7. https://docs.vulkan.org/spec/latest/chapters/renderpass.html
  8. https://docs.vulkan.org/spec/latest/chapters/raytraversal.html
  9. https://docs.vulkan.org/guide/latest/tile_based_rendering_best_practices.html
  10. https://docs.vulkan.org/features/latest/features/proposals/VK_EXT_mesh_shader.html
  11. https://docs.vulkan.org/refpages/latest/refpages/source/VK_KHR_dynamic_rendering.html
  12. https://docs.vulkan.org/refpages/latest/refpages/source/VK_KHR_dynamic_rendering_local_read.html
  13. https://docs.vulkan.org/refpages/latest/refpages/source/VkSubpassMergeStatusEXT.html
  14. https://registry.khronos.org/vulkan/specs/latest/man/html/VK_EXT_mesh_shader.html
  15. https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/performance/swapchain_images

三、厂商与工具文档(Arm / Qualcomm / Imagination / NVIDIA / RenderDoc)

  1. https://learn.arm.com/learning-paths/mobile-graphics-and-gaming/voice-assistant/8-performance-with-streamline/
  2. https://community.arm.com/arm-community-blogs/b/mobile-graphics-and-gaming-blog/posts/mali-bifrost-family-performance-counters
  3. https://community.arm.com/arm-community-blogs/b/mobile-graphics-and-gaming-blog/posts/mali-gpu-drivers-and-android-gpu-inspector
  4. https://community.arm.com/arm-community-blogs/b/tools-software-ides-blog/posts/profiling-cocos2d-x-game-engine----ds-5-streamline-case-study
  5. https://developer.arm.com/documentation/101897/latest
  6. https://docs.qualcomm.com/doc/80-70018-19SC/topic/graphics_developer_tools.html
  7. https://docs.qualcomm.com/bundle/publicresource/topics/80-71528-1/snapdragon_profiler.html
  8. https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html
  9. https://docs.imgtec.com/tools-manuals/pvrtune-manual/html/pvrtune-manual/topics/introduction.html
  10. https://zeux.io/data/gdc2020_pvr.pdf
  11. https://imagination-technologies-cloudfront-assets.s3.eu-west-1.amazonaws.com/website-files/documents/PowerVR+Hardware.Architecture+Overview+for+Developers.pdf
  12. https://images.nvidia.com/content/geforce/technologies/frameview/frameview-1-7-user-guide-web-version.pdf
  13. https://developer.nvidia.com/blog/advanced-api-performance-variable-rate-shading/
  14. https://renderdoc.org/docs/index.html
  15. https://renderdoc.org/docs/window/performance_counter_viewer.html

四、引擎侧文档(Unreal / Unity / Godot)

  1. https://docs.unrealengine.com/5.2/en-US/rendering-features-for-mobile-games-in-unreal-engine/
  2. https://dev.epicgames.com/documentation/en-us/unreal-engine/frame-pacing-for-mobile-devices-in-unreal-engine
  3. https://docs.unity3d.com/2023.2/Documentation/Manual/TimeFrameManagement.html
  4. https://docs.godotengine.org/en/stable/tutorials/scripting/debug/the_profiler.html
  5. https://docs.godotengine.org/en/stable/engine_details/development/profiling/index.html
  6. https://docs.godotengine.org/en/latest/tutorials/3d/variable_rate_shading.html

五、测量方法论、统计与第三方实测

  1. https://www.capframex.com/blog/post/Explanation%20of%20different%20performance%20metrics
  2. https://www.capframex.com/news/detail/New%20version%201.5.3
  3. https://truebottleneck.com/guides/1-percent-lows/
  4. https://learn.padho.ai/wiki/a-b-testing-for-performance
  5. https://www.fosdem.org/2026/events/attachments/8AS3XD-how-to-reliably-measure-software-performance/slides/267404/presentat_rtufkjn.pdf
  6. https://google.github.io/benchmark/tools.html
  7. https://kindatechnical.com/testing-non-deterministic-systems/statistical-decision-making-for-release-gates.html
  8. https://benchmarks.ul.com/en/support/benchmark-rules
  9. https://www.notebookcheck.com/Wie-wir-testen.1948.0.html
  10. https://www.androidperformance.com/2026/05/04/Android-Perfetto-16-GPU-Power-Counters/
  11. https://interplayoflight.wordpress.com/2025/05/05/meshlets-and-mesh-shaders/
  12. https://krupitskas.com/posts/modern_culling_techniques/
  13. https://www.tmarrec.dev/posts/mesh-shaders-and-meshlet-culling.html
  14. https://linebender.org/wiki/gpu/mesh-shaders/
  15. https://en.wikipedia.org/wiki/Variable_rate_shading

六、Wikimedia Commons:外部素材逐张溯源

  1. https://commons.wikimedia.org/wiki/File:Boxplot_vs_PDF.png
  2. https://commons.wikimedia.org/wiki/File:Beispielboxplot.png
  3. https://commons.wikimedia.org/wiki/File:2_box_plots_-_fr.jpg
  4. https://commons.wikimedia.org/wiki/File:Vsync_diagram.svg
  5. https://commons.wikimedia.org/wiki/File:Confidence_intervals_visual.jpg
  6. https://commons.wikimedia.org/wiki/File:Normal_distribution_50%25_CI_illustration.svg
  7. https://commons.wikimedia.org/wiki/File:ComparisonConfidenceIntervals.svg
  8. https://commons.wikimedia.org/wiki/File:Comparing_the_Standard_Normal_Distribution_and_Student%27s_T_Distribution.png
  9. https://commons.wikimedia.org/wiki/File:Voltage_versus_frequency_scaling_energy_tr.svg
  10. https://commons.wikimedia.org/wiki/File:Faktor-mediator-confound.png
  11. https://commons.wikimedia.org/wiki/File:Confounding.svg
  12. https://commons.wikimedia.org/wiki/File:Triple_buffering.png
  13. https://commons.wikimedia.org/wiki/File:Double_buffering.png
  14. https://commons.wikimedia.org/wiki/File:Comparison_double_triple_buffering.svg
  15. https://commons.wikimedia.org/wiki/File:Screen_tearing_on_DOSV.png
  16. https://commons.wikimedia.org/wiki/File:AllanDeviation.svg
  17. https://commons.wikimedia.org/wiki/File:Confidence_interval_by_Standard_deviation.svg
  18. https://commons.wikimedia.org/wiki/File:Confidence_intervall_normal_dist.svg
  19. https://commons.wikimedia.org/wiki/File:P-value_in_statistical_significance_testing.svg
  20. https://commons.wikimedia.org/wiki/File:Repeated_Measures_ANOVA_Example.png
  21. https://commons.wikimedia.org/wiki/File:Main-sources-of-Type-B-uncertainty.svg
  22. https://commons.wikimedia.org/wiki/File:Banakuntza_normala_histograma_01.png
  23. https://commons.wikimedia.org/wiki/File:Videosignal_vsync.jpg
  24. https://commons.wikimedia.org/wiki/File:QAS_Sample_Histogram_Chart_of_Gas_Expenses.png

七、Commons 检索路径(类目枚举留痕)

  1. https://commons.wikimedia.org/wiki/Category:Frame_rate
  2. https://commons.wikimedia.org/wiki/Category:Benchmarking
  3. https://commons.wikimedia.org/wiki/Category:Computer_performance
  4. https://commons.wikimedia.org/wiki/Category:Metrics
  5. https://commons.wikimedia.org/wiki/Category:Statistics
  6. https://commons.wikimedia.org/wiki/Category:Statistical_charts
  7. https://commons.wikimedia.org/wiki/Category:Data_visualization
  8. https://commons.wikimedia.org/wiki/Category:Box_plots
  9. https://commons.wikimedia.org/wiki/Category:Normal_distribution
  10. https://commons.wikimedia.org/wiki/Category:Confounding
  11. https://commons.wikimedia.org/wiki/Category:Computer_cooling
  12. https://commons.wikimedia.org/wiki/Category:Display_technology
  13. https://commons.wikimedia.org/wiki/Category:Graphics_processing_units
  14. https://commons.wikimedia.org/wiki/Category:Analysis_of_variance
  15. https://commons.wikimedia.org/wiki/Category:Measurement
  16. https://commons.wikimedia.org/wiki/Category:Metrology
  17. https://commons.wikimedia.org/wiki/Category:Video_game_development
  18. https://commons.wikimedia.org/wiki/Category:Histograms

附录 · 图片版权

本篇共 142 张图:外部真实素材 29 张、自绘示意图 113 张。外部素材逐张核过许可,可按来源页回溯;自绘示意图由本系列按同一份色板生成。

Wikimedia Commons(逐文件许可)

文件 章 作者 许可 来源页
ext_boxplot_vs_pdf.png 一 The original uploader was Jhguch at English Wikipedia. CC BY-SA 2.5 来源页
ext_boxplot_picture.png 一 The Twists and Turns CC0 来源页
ext_dvfs_voltage_frequency.svg 六 Oğuz Ergin CC BY-SA 4.0 来源页
ext_comparison_ci.png 五 KurtSchwitters, vector Habitator terrae CC BY-SA 3.0 来源页
ext_confidence_intervals.jpg 五 Rohanukhade CC0 来源页
ext_normal_ci.png 五 FRuDIxAFLG CC BY-SA 4.0 来源页
ext_t_vs_normal.png 五 Kindredgrey CC BY-SA 4.0 来源页

Android 开发者文档(站点其他内容 CC BY 2.5)

文件 章 作者 许可 来源页
ext_android_adpf_post.png 八 Android Developers CC BY 2.5 来源页
ext_android_adpf_pre.png 八 Android Developers CC BY 2.5 来源页
ext_android_agi_system_profile.png 三 Android Developers CC BY 2.5 来源页
ext_android_frame_lifecycle_tracks.png 三 Android Developers CC BY 2.5 来源页
ext_android_gc.png 五 Android Developers CC BY 2.5 来源页
ext_android_janky_frames_detailed.png 二 Android Developers CC BY 2.5 来源页
ext_android_jd_all_frames.png 二 Android Developers CC BY 2.5 来源页
ext_android_opt1.png 七 Android Developers CC BY 2.5 来源页
ext_android_opt6.png 七 Android Developers CC BY 2.5 来源页
ext_android_opt7.png 七 Android Developers CC BY 2.5 来源页
ext_android_power_rails.png 六 Android Developers CC BY 2.5 来源页
ext_android_trace_events_ui_renderthread.png 三 Android Developers CC BY 2.5 来源页
ext_android_vitals_thread_sleep.png 五 Android Developers CC BY 2.5 来源页

Perfetto 项目(Apache-2.0)

文件 章 作者 许可 来源页
ext_perfetto_app_timelines.png 二 Perfetto 项目 Apache-2.0 来源页
ext_perfetto_cpu_frequency.png 四 Perfetto 项目 Apache-2.0 来源页
ext_perfetto_gpu_by_process.png 三 Perfetto 项目 Apache-2.0 来源页
ext_perfetto_gpu_counters.png 四 Perfetto 项目 Apache-2.0 来源页
ext_perfetto_gpu_tracks.png 三 Perfetto 项目 Apache-2.0 来源页
ext_perfetto_timeline_tracks.png 三 Perfetto 项目 Apache-2.0 来源页

Khronos Vulkan-Samples(Apache-2.0)

文件 章 作者 许可 来源页
ext_khronos_swapchain_double.png 二 Khronos Group Apache-2.0 来源页
ext_khronos_swapchain_triple.png 二 Khronos Group Apache-2.0 来源页

Godot 引擎文档(CC BY 3.0)

文件 章 作者 许可 来源页
ext_godot_profiler.png 三 Godot Engine 文档团队 CC BY 3.0 来源页

不可转载声明:Arm、Qualcomm、Apple、Imagination、NVIDIA、Epic、Unity、Microsoft 的图片一律不得转载;其数据可在标注口径后引用,图由本系列自绘。

七篇写到这里,机制、取舍与证法都摆上了桌。把这一路的线索收成最后一张图,这个系列就到这里。

七篇收成一条「可验证」的主线
图 7-140收束图:从物理约束到分块与带宽、到功耗与热、到算法取舍、最后到测量与门禁,七篇收成一条「可验证」的主线@ 本系列绘制

相关链接

  • 上一篇:【移动端图形与功耗】(六) 现代特性落地:VRS、网格着色与移动光追
  • 下一篇(预告):下一个系列(题名待定,方向为渲染工程的实践与判定)

With Best Wishes.