29.性能优化-CPU-脚本-计时判断代码执行效率-Unity的Time类
29.1 知识点
为什么要计时判断代码执行效率
游戏开发中性能资源有限,若某段代码耗时过长,会导致帧率下降、卡顿或掉帧,从而影响玩家体验。通过计时可以更细颗粒度地找出是哪段代码导致帧率下降。
虽然 Profiler 能帮助排查耗时元凶,但手动实现代码计时仍有其价值:
Profiler 在正式发布版中一般不包含
手动计时代码可以在正式版本中用于日志上报、自动分析等。Profiler 不太适合精细比较同一段代码在不同算法下的微秒级表现
手动计时代码可以针对性地做细颗粒度比较。Profiler 不太适合做批量性能测试、基准对比
手动计时可以把一个算法跑指定次数,计算平均耗时、最大耗时等。
等等。
计时判断代码执行效率的基本原理
原理:记录一段代码执行前后的时间差,从而估算其运行耗时。
说人话:耗时 = 结束时间 - 开始时间。
对应代码形式如下:
float startTime = 获取开始时间;
// 执行要测的逻辑
float endTime = 获取结束时间;
float spendTime = endTime - startTime; // 耗时
测试建议:
- 多次执行、取平均值,减少偶然波动。
- 不要在初始化阶段测,等消耗稳定后(例如通过按键)再触发测试。
- 不要在测试循环内使用 Unity 控制台打印相关 API;应在测试结束后再输出,或只记录数据。
利用Unity的Time类计时
利用 Time 类中「游戏启动以来的时间」进行计算,常用 **Time.realtimeSinceStartup**(不受 Time.timeScale 影响)。单位为秒、带小数,精度约毫秒级,适合一般耗时统计。
示例代码:
float startTime = Time.realtimeSinceStartup;
// 执行要测的逻辑(不要在这里面用 Debug.Log,会拉高耗时)
// ... 你的代码 ...
float spendTime = (Time.realtimeSinceStartup - startTime) * 1000f; // 转为毫秒,1s = 1000ms
Util.Log("耗时 " + spendTime + " ms");
要点:在测试循环外再输出结果,或通过封装好的 Util.Log 等记录,避免在循环内直接 Debug.Log 影响测量。

利用C#机制封装一个计时类
可以利用 C# 的 using 语句块:using 包裹的对象在离开作用域时会自动调用销毁逻辑(执行 Dispose),因此可以在构造函数里记录开始时间、在 Dispose 里计算耗时并输出,这样写出来的计时代码简洁、不易漏写结束计时。
实现:CustomUnityTimer 实现 IDisposable,构造时记录测试名称、测试次数和开始时间,Dispose 时用 Time.realtimeSinceStartup 算总耗时并转为毫秒,再输出总耗时和平均每次耗时。
using UnityEngine;
using System;
public class CustomUnityTimer : IDisposable
{
private string _name; // 本次测试的名字
private uint _num; // 执行逻辑多少次
private float _startTime; // 计时开始时间
public CustomUnityTimer(string name, uint num)
{
_name = name;
_num = num;
if (_num == 0) _num = 1; // 至少执行 1 次
_startTime = Time.realtimeSinceStartup;
}
public void Dispose()
{
float spendTime = (Time.realtimeSinceStartup - _startTime) * 1000f; // 转为 ms
Util.Log(string.Format("{0}计时结束,共耗时{1}ms,一共测试{2}次,每次平均耗时{3}ms",
_name, spendTime, _num, spendTime / _num));
}
}
使用方式:用 using 包裹要测的逻辑即可;离开作用域时自动调用 Dispose 并打印结果。
uint num = 10000; // 测试次数
using (new CustomUnityTimer("某段逻辑耗时测试", num))
{
for (int i = 0; i < num; i++)
{
// 换成自己要测的函数或代码逻辑
DoSomething();
}
}
// 离开 using 时自动调用 Dispose,输出:共耗时 xx ms,平均每次 xx ms
建议在消耗稳定后通过按键等触发上述代码,而不是在 Start 里直接跑,以便得到更稳定的测试结果。

29.2 知识点代码
CustomUnityTimer.cs(计时封装示例)
using UnityEngine;
using System;
/// <summary> 用于对逻辑执行计时的性能分析类 </summary>
public class CustomUnityTimer : IDisposable
{
private string _name; /* 本次测试的名字 */
private uint _num; /* 执行逻辑多少次 */
private float _startTime; /* 计时开始时间 */
public CustomUnityTimer(string name, uint num)
{
_name = name;
_num = num;
if (_num == 0)
_num = 1; /* 避免传入 0,至少执行 1 次 */
_startTime = Time.realtimeSinceStartup;
}
public void Dispose()
{
float spendTime = (Time.realtimeSinceStartup - _startTime) * 1000f; /* 转为 ms */
Util.Log(string.Format("{0}计时结束,共耗时{1}ms,一共测试{2}次,每次平均耗时{3}ms",
_name, spendTime, _num, spendTime / _num));
}
}
Lesson29_性能优化_CPU_脚本_计时判断代码执行效率_Unity的Time类.cs
using UnityEngine;
public class Lesson29_性能优化_CPU_脚本_计时判断代码执行效率_Unity的Time类 : MonoBehaviour
{
void Start()
{
#region 知识点一 为什么要计时判断代码执行效率
/*
* 游戏开发中性能资源有限,某段代码耗时过长会导致帧率下降、卡顿或掉帧,影响体验。
* 通过计时可以更细颗粒度地找出是哪段代码导致帧率下降。
* 手动计时的好处:发布版可做日志上报与自动分析;可做微秒级、细颗粒度比较;
* 可跑指定次数做批量测试,算平均/最大耗时等。
*/
#endregion
#region 知识点二 计时判断代码执行效率的基本原理
/*
* 原理:记录代码执行前后的时间差,估算运行耗时。耗时 = 结束时间 - 开始时间。
* 测试建议:多次执行取平均;不在初始化时测,待稳定后通过按键触发;不在测试循环内用控制台打印,测试后再输出或只记录数据。
*/
#endregion
#region 知识点三 利用Unity的Time类计时
/*
* 使用 Time.realtimeSinceStartup(不受 Time.timeScale 影响),单位秒、带小数,毫秒级精度。
* 开始时记录 startTime,逻辑结束后 (Time.realtimeSinceStartup - startTime) * 1000f 即毫秒数。
*/
float startTime = Time.realtimeSinceStartup;
/* 执行一段逻辑代码 */
Debug.Log("12312312");
float spendTime = (Time.realtimeSinceStartup - startTime) * 1000f;
Util.Log("Debug打印耗时" + spendTime + "ms");
#endregion
#region 知识点四 利用C#机制封装一个计时类
/* 使用 C# 的 using 语句块,离开作用域时自动调用 Dispose,在构造时记开始时间、Dispose 时算耗时并输出 */
#endregion
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
uint num = 10000;
using (new CustomUnityTimer("Debug耗时测试", num))
{
for (int i = 0; i < num; i++)
{
/* 换成自己要测的函数或代码逻辑 */
Debug.Log("123123");
}
}
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com