UPL5-1——计时判断代码执行效率

本章代码关键字

1
2
3
4
5
6
7
Time.realtimeSinceStartup                // 得到Unity程序从启动以来的时间
Stopwatch // 用于高精度计时的工具类,可以精确地测量代码执行的时间
Stopwatch.StartNew() // 该方法会返回一个Stopwatch对象,并开始计时
stopwatch.Stop() // 该方法通过计时对象调用,将停止计时
stopwatch.ElapsedMilliseconds // 通过计时对象调用,获取耗时时间(毫秒),返回类型为 long,不使用与小于 1ms 的时间获取
stopwatch.Elapsed.TotalMilliseconds // 该成员通过计时对象调用,获取耗时时间(毫秒),返回类型为 double,可以精确到微秒级
stopwatch.Restart() // 清零重启计时

为什么要计时判断代码执行效率

游戏开发中性能资源有限,如果某些代码段耗时过长,会导致帧率下降、卡顿或掉帧,从而导致玩家体验变差
通过计时我们可以更“细颗粒度”找出是哪段代码导致帧率下降

虽然 Profiler 可以帮助我们排查耗时的元凶,但是手动实现代码计时依然可以给我们带来以下好处

  1. Profiler 在正式发布版中一般不包含,而手动计时代码可以在正式版本中用于日志上报、自动分析等
  2. Profiler 不太适合精细比较同一段代码在不同算法下的微秒级表现,而手动计时代码可以让我们针对性的“细颗粒度”的进行比较
  3. Profiler 不太适合做批量性能测试、基准对比,而手动计算可以把一个算法跑指定次数,计算平均耗时、最大耗时等等

等等

计时判断代码执行效率的基本原理

原理:记录一段代码执行前后的时间差,从而估算它的运行耗时
说人话:耗时 = 结束时间 - 开始时间

测试建议:

  1. 多次执行计算 求平均值
  2. 不要在初始化中测试,待消耗稳定后,通过按键启用
  3. 不要在测试中使用 Unity 的控制台打印相关 API,应在完成测试后再输出 或 直接记录数据

利用 Unity 的 Time 类计时(精度低)

利用 Time​ 类中游戏启动以来的时间进行计算
Time.realtimeSinceStartup​ 得到的时间单位是秒,带小数,是毫秒级的,精度适中(不受 Time.timeScale 缩放影响)

1
2
3
4
5
6
float startTime = Time.realtimeSinceStartup;
//执行一段逻辑代码
Debug.Log("12312312");

float spendTime = (Time.realtimeSinceStartup - startTime) * 1000f;
Debug.Log($"Debug打印耗时: {spendTime} ms");

image

利用 C# 机制封装一个计时类

利用 C# 的 using​ 语句块,using​ 语句块包裹的对象使用完后会调用销毁逻辑(执行 Dispose 函数),可简化操作逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;
using UnityEngine;

public class UnityTimer : IDisposable
{
private string _name; // 计时任务的名字
private uint _num; // 计时任务的执行次数
private float _startTime; // 计时开始时间


public UnityTimer(string name, uint num)
{
_name = name;
if (num == 0)
num = 1;
_num = num;
_startTime = Time.realtimeSinceStartup;
}

public void Dispose()
{
float spendTime = (Time.realtimeSinceStartup - _startTime) * 1000f;
DebugUtil.Log($"{_name} 计时结束,共耗时{spendTime}ms,一共测试{_num}次,每次耗时{spendTime / _num}ms");
}
}
1
2
3
4
5
6
7
8
using (new UnityTimer("Debug耗时测试", 10000))
{
//执行一段逻辑代码
for (int i = 0; i < 10000; i++)
{
Debug.Log("12312312");
}
}

image

C# 的 Stopwatch 类

Stopwatch​ 类是 C# 中提供的一个用于高精度计时的工具类,它可以精确地测量代码执行的时间,常用于性能分析和调试
所属命名空间:System.Diagnostics(诊断)

该类提供毫秒甚至微妙级别的计时精度

常用API:

  1. 利用类中提供的静态方法开启一个计时 Stopwatch.StartNew()

    该方法会返回一个 Stopwatch 对象,并开始计时

  2. 停止计时 stopwatch.Stop()

    该方法通过计时对象调用,将停止计时

  3. 获取计时

    • stopwatch.ElapsedMilliseconds

      该成员通过计时对象调用,获取耗时时间(毫秒)
      返回类型为 long,不使用与小于 1ms 的时间获取

    • stopwatch.Elapsed.TotalMilliseconds

      该成员通过计时对象调用,获取耗时时间(毫秒)
      返回类型为 double,可以精确到微秒级

  4. 重启计时 stopwatch.Restart()

    该方法通过计时对象调用,会清零重启计时

利用 C# 机制封装一个计时类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;
using System.Diagnostics;

public class CodeTimer : IDisposable
{
private string _name; // 计时任务的名字
private uint _num; // 计时任务的执行次数
private Stopwatch _watch; // 计时对象

public CodeTimer(string name, uint num)
{
_name = name;
if (num <= 0)
num = 1;
_num = num;
_watch = Stopwatch.StartNew();
}

public void Dispose()
{
_watch.Stop();
double spendTime = _watch.Elapsed.TotalMilliseconds;
DebugUtil.Log($"{_name} 计时结束,共耗时{spendTime}ms,一共测试{_num}次,每次耗时{spendTime / _num}ms");
}
}
1
2
3
4
5
6
7
8
using (new CodeTimer("Debug耗时测试", 10000))
{
//执行一段逻辑代码
for (int i = 0; i < 10000; i++)
{
Debug.Log("12312312");
}
}

image