# 1. 为什么需要?
这个性能工具的作用是,能够让我们自己知道,每个阶段分别耗时多少,并且哪个阶段最慢,用了哪些步骤。
# 2. 核心
这个轻量级性能分析工具的本质,其实就是一个树形结构的计时器。每个节点都是一个 Profiler 的实例,可以
# 3. 文件逻辑组成
# 3.1 静态成员
1 2 3 4 5 6 7 8
| private static readonly Stopwatch ms_Stopwatch = Stopwatch.StartNew();
private static readonly StringBuilder ms_StringBuilder = new StringBuilder();
private static readonly List<Profiler> ms_Stack = new List<Profiler>();
|
# 3.2 实例成员
1 2 3 4 5 6
| private List<Profiler> m_children; private string m_Name; private int m_Level; private long m_Timestamp; private long m_Time; private int m_Count;
|
其实可以简单理解为,这些字段就是 “树节点 + 计时器” 的组合,即每个节点都拥有自己的计时,并且拥有各个的子节点。
# 4. 主要方法解析
# 4.1 构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public Profiler(string name) { m_children = null; m_Name = name; m_Level = 0; m_Timestamp = -1; m_Time = 0; m_Count = 0; }
private Profiler(string name, int level) : this(name) { m_Level = level; }
|
构造函数用于创建新的节点,并且在下面为了区分子节点,重载了一个方法
# 4.2 CreateChild
1 2 3 4 5 6 7 8 9 10 11 12
|
public Profiler CreateChild(string name) { if (m_children == null) { m_children = new List<Profiler>(); } Profiler profiler = new Profiler(name, m_Level + 1); m_children.Add(profiler); return profiler; }
|
# 4.3 计时器核心操作
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
public void Reset() { m_Timestamp = -1; m_Time = 0; m_Count = 0; if (m_children == null) return; for (int i = 0; i < m_children.Count; ++i) { m_children[i].Reset(); }}
public void Start() { if (m_Timestamp != -1) { throw new Exception($"{nameof(Profiler)} {nameof(Start)} error, repeat start, name: {m_Name}"); } m_Timestamp = ms_Stopwatch.ElapsedTicks; }
public void Restart() { Reset(); Start(); }
public void Stop() { if (m_Timestamp == -1) { throw new Exception($"{nameof(Profiler)} {nameof(Stop)} error, repeat stop, name: {m_Name}"); } m_Time += ms_Stopwatch.ElapsedTicks - m_Timestamp; m_Count += 1; m_Timestamp = -1; }
|
# 4.4 核心:格式化操作
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
|
private void Format() { ms_StringBuilder.AppendLine(); for (int i = 0; i < m_Level; ++i) { ms_StringBuilder.Append(i < m_Level - 1 ? "| " : "|--"); } ms_StringBuilder.Append(m_Name); if (m_Count <= 0) return; ms_StringBuilder.Append(" ["); ms_StringBuilder.Append("Count: "); ms_StringBuilder.Append(m_Count); ms_StringBuilder.Append(", Time: "); float ms = (float)m_Time / TimeSpan.TicksPerMillisecond; float sec = (float)m_Time / TimeSpan.TicksPerSecond; float min = (float)m_Time / TimeSpan.TicksPerMinute; ms_StringBuilder.Append($"{ms:F2} 毫秒, "); ms_StringBuilder.Append($"{sec:F2} 秒, "); ms_StringBuilder.Append($"{min:F4} 分钟"); ms_StringBuilder.Append("]"); }
public override string ToString() { ms_StringBuilder.Clear(); ms_Stack.Clear(); ms_Stack.Add(this); while (ms_Stack.Count > 0) { int index = ms_Stack.Count - 1; Profiler profiler = ms_Stack[index]; ms_Stack.RemoveAt(index); profiler.Format(); List<Profiler> children = profiler.m_children; if (children == null) continue; for (int i = children.Count - 1; i >= 0; --i) { ms_Stack.Add(children[i]); } } return ms_StringBuilder.ToString(); }
|