评估性能问题

学会用 Unity Profiler 建立性能基线,判断 CPU/GPU 瓶颈,再动手优化。

学习目标

  • 能解释「Profile First」在 Unity 优化流程中的位置,以及为什么盲改代码往往无效
  • 能说出 Profiler 四大模块(CPU、Rendering、Memory、Audio)各自回答什么问题
  • 能根据 CPU ms 与 GPU ms 判断当前帧更可能是 CPU bound 还是 GPU bound,并回答:没有基线数据时,你为什么不该先改 Shader?

原书边界:Chapter 1

Packt 第三版把本章命名为 Evaluating Performance Problems。官方目录列出的核心范围如下;这些条目共同构成本章的一对一边界:

  • Gathering profiling data using the Unity Profiler:纳入本章实验与验收,不拆到别章重复计数。
  • Best approaches to performance analysis:纳入本章实验与验收,不拆到别章重复计数。
  • Final thoughts on profiling and analysis:纳入本章实验与验收,不拆到别章重复计数。

固定目标设备、场景、画质和采样窗口,再把帧时间归因到 CPU、GPU、内存或等待。在 Unity 6 中可结合 Profiler、Profile Analyzer、Memory Profiler 与目标平台工具,但原书的测量、归因、复测闭环不变。 因此,本章既保留 2019/2020 语境,也明确标出迁移到现代 Unity 时哪些是稳定原理、哪些只是版本相关接口。

游戏卡了,先别改代码

你的场景一运行就掉帧:角色多了就幻灯片,切场景要转半天。第一反应往往是「是不是某段脚本写坏了?」「把画质调低试试?」——然后开始在项目里到处改,改完却有时快、有时更慢,还说不清到底哪一刀有效。

这章要解决的,就是在动刀之前先量清楚:慢在哪里、慢多少、对照尺是什么。没有测量就优化,就像蒙着眼修钟表——碰运气,还很容易把别的地方搞坏。

性能优化有一条铁律:先拿数据,再动刀。没有对照尺,你后面的每一章优化技巧都可能用错地方。

测量先于优化

就是这条铁律的名字。下面你会反复看到它。

是整条优化流水线的起点。你可以把它想成工厂流水线里的「首件检验」——先记下标准品的数据,后面每道工序改完都要和这张单子比,而不是凭感觉说「好像快了一点」。

1建立基线Profiler 记录固定场景·画质记录 FPS/耗时2定位瓶颈CPU/GPU 模块找最热函数或最重 Draw3验证修复对比前后同场景复测看指标下降4回归测试防回退多设备·长时纳入 CI 基线四步循环:建立基线 → 定位瓶颈 → 验证修复 → 回归测试
Profile First:先测量再优化。四步循环避免「盲改代码却越改越慢」。

整条工作流分四步:建立基线 → 定位瓶颈 → 验证修复 → 回归测试。本章先把前两步的工具和概念铺好;后两步会在你动手改脚本、改渲染时反复用到。

为什么「感觉快了」不算数

玩家说「有点卡」、你自己觉得「好像顺了一点」,都是主观感受。帧时间差 2ms,人眼未必分辨得出;但 200 个这样的「小优化」堆在一起,或者某次「优化」其实增加了隐藏开销,只有数字能告诉你真相。基线就是把这些数字钉在墙上。

Profiler 窗口:四块仪表盘

Unity 内置的 把一帧里发生的事拆成纵向四块,像四块叠在一起的仪表盘:

Profiler — Play ModeRecord · Deep Profile0ms ——— 16ms (60 FPS) ——— 33msCPU Usage脚本·物理·动画RenderingDraw Call·GPU 时间Memory堆·纹理·网格Audio混音·DSP四大模块各司其职——下文逐块拆开看
Unity Profiler 主窗口:纵向堆叠的 CPU、渲染、内存、音频模块,每帧采样耗时。

CPU Usage:脚本与主线程

回答「哪段逻辑在吃 CPU」。主线程上的 UpdateFixedUpdate、物理、动画、UI 重建都会出现在这里。Self 时间是函数自身耗时,Total 包含它调用的子函数——找热点时通常先看 Total 最高的条目。

Profiler — Play ModeRecord · Deep Profile0ms ——— 16ms (60 FPS) ——— 33msCPU Usage脚本·物理·动画RenderingDraw Call·GPU 时间Memory堆·纹理·网格Audio混音·DSP
Unity Profiler 主窗口:纵向堆叠的 CPU、渲染、内存、音频模块,每帧采样耗时。

Rendering:GPU 与绘制

回答「这一帧画了多狠」。Draw Call 和 Batches 过高、GPU 时间顶满帧预算,往往意味着合批不足、Overdraw 严重或着色器过重。它和 CPU Usage 是两条线——CPU 忙不等于 GPU 忙

Profiler — Play ModeRecord · Deep Profile0ms ——— 16ms (60 FPS) ——— 33msCPU Usage脚本·物理·动画RenderingDraw Call·GPU 时间Memory堆·纹理·网格Audio混音·DSP
Unity Profiler 主窗口:纵向堆叠的 CPU、渲染、内存、音频模块,每帧采样耗时。

Memory 与 Audio

模块看谁占了多少 RAM——纹理、网格、AudioClip、托管堆(C# 对象)各有多大,有没有只增不减的泄漏趋势。移动平台上内存触顶会直接被杀进程。

模块在音频较重的项目里才显眼;多数卡顿先从 CPU Usage 和 Rendering 查起,但背景音乐、大量 3D 音源也会占 CPU。

Profiler — Play ModeRecord · Deep Profile0ms ——— 16ms (60 FPS) ——— 33msCPU Usage脚本·物理·动画RenderingDraw Call·GPU 时间Memory堆·纹理·网格Audio混音·DSP
Unity Profiler 主窗口:纵向堆叠的 CPU、渲染、内存、音频模块,每帧采样耗时。

录制时勾选 Deep Profile 可以看到每个 C# 函数的开销,但开销极大、会严重拖慢帧率,只适合短场景、小范围排查——不要用 Deep Profile 的数据当帧率基线。

CPU bound 还是 GPU bound?

一帧的总时间受两侧限制: 时,主线程忙到接近帧预算(60 FPS 约 16.6ms),GPU 还在等命令; 时则相反,Rendering 里 GPU 时间顶满,CPU 还有余量。

16.6ms 帧预算 (60 FPS)CPU脚本·物理GPU绘制·着色CPU 柱顶满预算 → CPU bound:先查脚本与主线程
对比 CPU ms 与 GPU ms:哪一侧更接近帧预算上限,就先优化哪一侧。

判断口诀:看 Profiler 里 CPU 主线程耗时和 GPU 时间(Rendering 模块)谁更接近帧预算上限,就先优化谁。注意 VSync 打开时 GPU 可能「等垂直同步」显示虚高——要在目标平台上、用与发布接近的画质设置测。

16.6ms 帧预算 (60 FPS)CPU脚本·物理GPU绘制·着色GPU 柱顶满预算 → GPU bound:先查渲染与 Overdraw
对比 CPU ms 与 GPU ms:哪一侧更接近帧预算上限,就先优化哪一侧。

两侧都高叫双瓶颈,先砍更高的那一侧;两侧都低却掉帧,查同步等待、加载卡顿或编辑器开销(要用 Development Build 在真机上测)。

Frame Debugger 与 Profile Analyzer

Profiler 告诉你「哪一类系统慢」;下面两个工具帮你看「这一帧具体画了什么 / 多次采样稳不稳定」。

Frame Debugger(菜单 Window → Analysis → Frame Debugger)逐帧列出每个 Draw Call:用的材质、网格、Shader Pass。适合查多余绘制、透明排序错误、本该合批却拆开的情况——Profiler 的 Rendering 只给总数,Frame Debugger 给清单。

Profile Analyzer 可导入多份 Profiler 采样文件,做均值、分位数、对比图表。单次采样可能撞上「运气好的一帧」;Analyzer 帮你看优化是否在多次运行、多设备上稳定生效,也是回归测试的得力助手。

动手:走一遍 Profiler 工作流

下面用分步演示把 Profile First 四步走一遍。每切一步,流程图会高亮当前阶段——建议对照你自己项目里一个最卡的场景,在编辑器里同步打开 Profiler 试一次。

猜一猜:如果跳过第 ① 步直接改代码,第 ③ 步「验证修复」时你会遇到什么麻烦?

分步1 / 4

① 建立基线:固定场景,录一轮 Profiler

1建立基线Profiler 记录固定场景·画质记录 FPS/耗时2定位瓶颈CPU/GPU 模块找最热函数或最重 Draw3验证修复对比前后同场景复测看指标下降4回归测试防回退多设备·长时纳入 CI 基线第 ① 步:在固定场景下用 Profiler 记下 FPS、CPU ms、GPU ms,建立对照尺
Profile First:先测量再优化。四步循环避免「盲改代码却越改越慢」。

选定一个固定测试场景(出生点、镜头、画质档位都记下来)。进入 Play,打开 Profiler,点 Record,跑 30–60 秒典型玩法。记下平均 FPS、CPU 主线程 ms、GPU ms、GC Alloc。把画质和场景名写进团队文档——这就是你的对照尺。

代码速查:打开 Profiler 与录制

Unity 没有「一行代码代替 Profiler」,但可以用脚本在测试构建里自动开始录制,方便 QA 复现:

// 放在测试场景的入口;仅 Development Build 生效
#if DEVELOPMENT_BUILD
void Start() {
    UnityEngine.Profiling.Profiler.logFile = "profilerlog.raw";
    UnityEngine.Profiling.Profiler.enabled = true;
}
#endif

录制的 .raw / .data 可在 Profile Analyzer 中导入对比。日常开发更常用编辑器菜单 Window → Analysis → Profiler(快捷键通常绑定在 Unity 版本说明里)。

容易踩的坑

小结

  • Profile First:先测量再优化,基线是对照一切改动的尺子
  • Profiler 四模块:CPU Usage(逻辑)、Rendering(绘制)、Memory(内存)、Audio(音频)
  • CPU bound / GPU bound 看哪一侧顶满帧预算,先优化瓶颈侧
  • Frame Debugger 逐帧看 Draw Call;Profile Analyzer 多样本统计对比
  • 验证与回归测试必须在固定场景、目标设备上重复进行

练习

问题 1(改 Demo 型) 在你项目里最卡的场景打开 Profiler,录 30 秒,写下 CPU 主线程 ms、GPU ms、Batches 三个数作为基线。然后只关闭一个你认为最可疑的脚本组件,同场景再录 30 秒。三次数字填表对比——你的改动有效吗?属于 CPU 侧还是 GPU 侧变化?

问题 2(问答型) 某关 CPU 主线程 14ms,GPU 4ms,目标 60 FPS。这是 CPU bound 还是 GPU bound?你下一步会打开 Profiler 的哪个模块、看什么?

问题 3(问答型) Frame Debugger 和 Profile Analyzer 分别解决什么问题?各举一个适合使用的场景。

名词解释

本章出现的专业名词,用大白话再讲一遍。

Profile First(先测量再优化)

动刀之前先用 Profiler 等工具采数据、建基线。没有对照尺的优化等于猜谜。详见本章「测量先于优化」。

性能基线

固定场景与画质下记录的一组 FPS、CPU ms、GPU ms 等数字,优化前后用同一条件复测。详见工作流第 ① 步。

Profiler

Unity 内置性能分析窗口,按帧展示 CPU、渲染、内存、音频。Play 时 Record 即可采样。详见「Profiler 窗口」一节。

CPU Usage

Profiler 模块,显示各线程与函数的 CPU 耗时,查脚本与引擎系统热点。详见「CPU Usage」小节。

Rendering

Profiler 模块,显示 Draw Call、Batches、GPU 时间等,查绘制压力。详见「Rendering」小节。

Memory

Profiler 模块,显示纹理、网格、托管堆等内存占用与分配。详见「Memory 与 Audio」。

Audio

Profiler 模块,显示音频混音与 DSP 的 CPU 开销。音频重的项目需关注。

CPU bound

帧时间主要受 CPU 限制,GPU 有空闲。应优先减脚本、物理、GC 等 CPU 工作。详见「CPU bound 还是 GPU bound」。

GPU bound

帧时间主要受 GPU 绘制限制。应优先减 Draw Call、Overdraw、着色器开销。详见「CPU bound 还是 GPU bound」。

资料与写作方式声明

本章以Packt《Unity Game Optimization》第3版权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…