由于以下原因我们考虑放弃unity profiler
- 效率过低(string作为key, 会导致GC.Alloc), 对性能干扰太大, 无法统计纳秒级精度.
- 在标记位达到一定数量后, 链接手机, 容易导致windows的内存不足而crash
但unity profiler的一大优点就是能够给出一帧的完整统计, 让我们知道总共有哪些系统, 开销是怎么样的. 我们需要自己对这一块进行统计, 不管均值优化还是毛刺优化都可以快速帮助我们定位到高开销模块.
1. Unity真实环境下的运行分布规律
1.Unity文档的一帧开销大致分布如下, 随着引擎更新, 这幅图未包含一些具体引擎实现细节, 只能作为参考
https://docs.unity3d.com/Manual/ExecutionOrder.html
2.FixedUpdate根据累计时间保障30次频率, 因此每帧次数不固定(0~30次之间)
3.在fixedupdate前还有较多其他的固定步骤, 如下图
4. Update与LateUpdate之间如下图, 不仅仅只是animation, 还包括其他
5. LateUpdate与SceneRendering之间存在大量琐碎模块更新, 如下图
6. OnGui一帧内可能运行多次, 原因仍然不清楚
7. 经测试, 在MonoBehaivour如下代码可以截取GPU信号, 但是此时激活的后续_begin的GameObject的物体, Awake和OnEnable会紧随Start()后续立即被调用期, 而并不是像文档上说的会在第二帧开始调用.
public class ProfilerStampEnd: SummerBehaivour { public GameObject _begin; public IEnumerator Start() { while (true) { yield return new WaitForEndOfFrame(); _begin.SetActive(true); } yield return null; } }
8. 渲染相关的统计因为Unity的OnRenderImage如果什么都不做也必须调用Graph.Blit将src拷贝到dest中, 否则屏幕将黑屏, 而在高分辨率的移动平台下, 一次空的Graph.Blit也将导致1920 *1024次显存读写操作, 实测约为1ms左右
2.最终物理设计
将Unity一帧划分为如下5个大部分
1. FixedUpdate模块
·包含逻辑/物理/ OnTrigger/OnCollision等事件
·在第一个FixedUpdate的脚本进行计时
·然后在yield WaitFixedUpdate结束, 统计区间间隔
2. Update模块
·包含所有Update更新, 动画, 特效粒子, NGUI, LateUpdate
·在第一个OnUpdate的脚本进行计时
·在最后一个LateUpdate的脚本结束
3. 相机裁减与DrawCall模块
·包含相机裁减/PostProcess/AmplifyBloom/Sunshine等渲染模块的更新
·在最后一个LateUpdate的脚本开始计时
·在第一个OnGui的脚本结束
4. OnGui模块
·我们逻辑调试用代码以及unity内部的一些debug内容(比如右下角标: development build的显示)
·在第一个OnGui的脚本开始标记计时
·在yield WaitForEndOfFrame结束标记
5.GameObject的初始化与销毁
·GameObject/MonoBehaivour的销毁模块 : 包括OnDisable/OnDestroy/ OnEnable
·在yield WaitForEndOfFrame开始
·在第一个FixedUpdate或者Update中结束计时(二个选项互斥)
·并不是每帧都会有