性能优化的第一步,是确定哪一部分工作消耗了帧预算。减少某个函数的执行时间,如果这个函数并非限制帧率的因素,最终体验可能不会改变。下面的流程以 Unity 6.0 官方 Profiler 文档为参考,具体模块和标记可能随平台与版本变化。
从帧时间预算开始
60 FPS 对应每帧约 16.67 毫秒,120 FPS 对应约 8.33 毫秒。平均 FPS 容易掩盖偶发长帧,因此应同时观察帧时间分布和持续时间较长的尖峰。一次资源加载或垃圾回收造成的长帧,可能比平均帧率的小幅变化更影响手感。
CPU 与 GPU 通常是流水工作的,不能简单把两个耗时相加当成最终帧时间。垂直同步、帧率上限、平台调度与线程同步也会产生等待。先确认目标帧率和采样场景,才能判断一段等待是性能瓶颈还是正常的帧率限制。
在目标设备上采样
Editor 中的 Play Mode 适合快速定位方向,但包含编辑器、Inspector 和 Profiler 本身的开销。最终判断应结合目标平台上的构建数据,特别是移动设备的热状态、图形 API 和实际分辨率。
- 固定一个可复现的场景,例如同样的敌人数、相机路径和画质设置。
- 使用 Development Build,并按需求连接 Profiler;记录 Unity 版本、设备和渲染设置。
- 等待必要的资源加载和初始化完成,区分首次运行开销与稳定运行开销。
- 采集正常帧与问题帧,并保留样本用于前后比较。
开发构建和 Profiler 自身仍会改变性能。找到问题并修改后,还应在接近发布的构建中复核体验。Deep Profiling 会为大量方法增加采样开销,适合缩小范围后的诊断,不宜直接把其耗时当成发布环境结果。
用 Timeline 解释主线程与等待
CPU Usage 模块的 Timeline 可以同时显示多条线程在时间轴上的工作。先查看长帧内主线程、渲染线程和工作线程的关系,再使用 Hierarchy 追踪某个样本的总耗时、自身耗时和调用次数。
- 脚本热点:查看是否存在高频调用、重复查询或某次突发处理。
- 渲染提交:关注相机、材质切换、对象可见性与管线处理成本。
- 线程等待:结合前后线程的工作判断等待原因,不能只看到名字就下结论。
- GC.Alloc:它反映托管分配,不能直接等同于这一帧已发生垃圾回收。
例如某些 Present 或同步相关标记出现很长等待,可能与 GPU 负载有关,也可能来自 VSync 或帧率限制。应结合 GPU 模块、目标平台支持情况和设置变化验证,而不是一看到等待就去重写脚本。
一次只验证一个主要假设
如果怀疑物理查询次数过多,可以先减少重复查询,再在同样场景重新采样;如果怀疑填充率,则可以临时降低渲染分辨率观察变化。这样的控制变量实验比同时修改十个系统更容易解释结果。
记录修改前后的主线程、GPU、托管分配和长帧表现,并说明测试条件。某项优化也可能把 CPU 开销转移成额外内存,或在另一个平台产生相反结果。最终保留的应是对目标设备和真实场景有收益的改动。
参考资料
本文根据以下资料整理撰写,详细规范与版本行为请以原文为准。