首页 > 其他分享 >【Unity】 HTFramework框架(四十)Debug的性能监控

【Unity】 HTFramework框架(四十)Debug的性能监控

时间:2022-12-13 15:07:53浏览次数:60  
标签:监控 test Unity GC ints Debug data HTFramework


更新日期:2021年4月22日。
Github源码:​​​[点我获取源码]​​​ Gitee源码:​​[点我获取源码]​

索引

  • ​​C#代码性能监控​​
  • ​​使用​​
  • ​​Debug的性能监控模式​​
  • ​​监控代码片段​​
  • ​​监控方法​​
  • ​​适用场景​​

C#代码性能监控

C#代码运行时的性能消耗主要体现在两个方面,1是​​时间消耗​​​,2是​​空间消耗​​。

  • 时间消耗我们可以理解为某一段代码执行所消耗的CPU时间;
  • 空间消耗也即是消耗的内存空间,在C#中,内存空间分为栈空间堆空间,当然堆空间也叫做托管堆空间,因为堆这块已经托管给了CLR,他会负责开辟堆空间清理堆空间,所以我们主要关心的就是开辟清理这两个过程,因为在C#中这两个过程就是造成性能瓶颈的根本原因,也就是所谓的GC。

使用

Debug的性能监控模式

监控代码片段

一、我们可以使用如下方式监控一个代码片段的执行性能:

void Start()
{
List<int> ints = new List<int>(1000000);

//开始监控
Main.m_Debug.BeginMonitor("计算一百万次");

int test = 0;
foreach (var i in ints)
{
test += i;
}

//结束监控
MonitorData data = Main.m_Debug.EndMonitor();

//打印监控日志
data.ToString().Info();
}

运行结果如下:

注意:
1.产生的堆内存垃圾:也即是在托管堆上开辟的托管空间;
2.触发GC次数:当CLR清理托管堆时也即是一次GC;
如果你的代码开辟的空间越多,或者导致的GC次数越多,则证明他们产生性能瓶颈的可能性越大。

这里虽然foreach遍历执行了一百万次整型加法运算,但并未产生堆内存垃圾。

【Unity】 HTFramework框架(四十)Debug的性能监控_List

二、然后我们改一下代码,将变量ints的赋值语句也放在监控代码段内:

void Start()
{
//开始监控
Main.m_Debug.BeginMonitor("计算一百万次");

List<int> ints = new List<int>(1000000);

int test = 0;
foreach (var i in ints)
{
test += i;
}

//结束监控
MonitorData data = Main.m_Debug.EndMonitor();

//打印监控日志
data.ToString().Info();
}

运行结果如下:

这里由于List是引用类型,new一个引用类型会在托管堆上开辟新的空间,也即是生成了一段内存垃圾(虽然他暂时还不是内存垃圾,但用完之后就是了),可见,一个整型的大小是4字节,一百万个整型正好是4百万字节,正好是4M字节!不过4M的垃圾并没有被CLR看在眼里,触发GC次数为0证明了他没有因为这点新增的垃圾就去启动回收操作。

【Unity】 HTFramework框架(四十)Debug的性能监控_HTFramework_02

三、然后我们再改一下代码,手动GC一次:

void Start()
{
//开始监控
Main.m_Debug.BeginMonitor("计算一百万次");

List<int> ints = new List<int>(1000000);

int test = 0;
foreach (var i in ints)
{
test += i;
}

//清理一次内存,将主动触发一次GC
Main.m_Resource.ClearMemory();

//结束监控
MonitorData data = Main.m_Debug.EndMonitor();

//打印监控日志
data.ToString().Info();
}

运行结果如下:

可以看到已经GC了一次,也即是回收了一次垃圾,但新增的堆内存垃圾仍然还有3M,很显然ints所指向的内存空间并没有被回收,CLR只是从其他地方收回了1M的空闲内存,因为CLR目前并不知道ints所指向的空间已经变成了垃圾,因为整个Start方法还没有结束,你还可以在后续调用ints,所以他还不是垃圾空间。

【Unity】 HTFramework框架(四十)Debug的性能监控_List_03


四、然后我们再改一下代码,将代码片段放在一个方法里面:

void Start()
{
//开始监控
Main.m_Debug.BeginMonitor("计算一百万次");

Test();

//清理一次内存,将主动触发一次GC
Main.m_Resource.ClearMemory();

//结束监控
MonitorData data = Main.m_Debug.EndMonitor();

//打印监控日志
data.ToString().Info();
}

private void Test()
{
List<int> ints = new List<int>(1000000);

int test = 0;
foreach (var i in ints)
{
test += i;
}
}

运行结果如下:

可以看到,GC了一次后,ints所生成的4M垃圾空间已经被回收掉了,为什么这样就能回收掉呢?因为,ints的作用域变为了Test方法,在GC回收时,Test已经执行完毕,CLR收到明确指令:ints指向的已经是一块垃圾空间,可以回收。

注意:我们对比一下代码的时间消耗,未触发GC的执行时间为:0.0005秒,触发一次GC的执行时间为:0.0166秒,很明显,这多出来的近30倍执行时间,便是GC带来的性能损耗,这就好比使用时间的代价换来了空间!

【Unity】 HTFramework框架(四十)Debug的性能监控_Unity_04

终上所述,我们监控一个代码片段的执行效率时,重点关心的就是他的执行时间和产生了多少的堆内存垃圾,当然,如果在监控过程中触发了GC,那么最后产生的堆内存垃圾数量就不一定准确了,因为可能有一些垃圾已经被回收了,只不过,如果你的代码总是频繁的在触发GC,那么你一定得考虑重构他们了!

监控方法

当然,Debug也支持在监控模式中运行某一个方法,如下:

void Start()
{
//监控模式执行Test
MonitorData data = Main.m_Debug.MonitorExecute(Test);

//打印监控日志
data.ToString().Info();
}

private void Test()
{
List<int> ints = new List<int>(1000000);

int test = 0;
foreach (var i in ints)
{
test += i;
}
}

适用场景

一、我们可以使用性能监控器监测一些带来性能瓶颈的行为,比如,如下这个极具性能损耗的行为:

void Start()
{
//开始监控
MonitorData data = Main.m_Debug.MonitorExecute(Test, "字符串累加");

//打印监控日志
data.ToString().Info();
}

private void Test()
{
//string累加一万次
string test = "";
for (int i = 0; i < 10000; i++)
{
test += "string";
}
}

运行结果如下:

此时产生的堆内存垃圾记录已然不准确了,因为已经触发了79次GC!

【Unity】 HTFramework框架(四十)Debug的性能监控_List_05


二、发现这个极大问题之后,我们即刻选择使用StringBuilder改进:

void Start()
{
//开始监控
MonitorData data = Main.m_Debug.MonitorExecute(Test, "StringBuilder累加");

//打印监控日志
data.ToString().Info();
}

private void Test()
{
//string累加一万次
StringBuilder builder = new StringBuilder();
string test = "";
for (int i = 0; i < 10000; i++)
{
builder.Append("string");
}
test = builder.ToString();
}

运行结果如下:

可以看到惊人的优化效果,只产生了262KB的垃圾,并且避免了触发GC!

【Unity】 HTFramework框架(四十)Debug的性能监控_C#性能检测_06


三、当然,我们也可以尝试使用string.Format来达到同样的效果:

void Start()
{
//开始监控
MonitorData data = Main.m_Debug.MonitorExecute(Test, "Format累加");

//打印监控日志
data.ToString().Info();
}

private void Test()
{
//同样是string累加一万次
string test = "";
for (int i = 0; i < 10000; i += 10)
{
test = string.Format("{0}{1}{1}{1}{1}{1}{1}{1}{1}{1}{1}", test, "string");
}
}

运行结果如下:

可以看到,string.Format虽然并不是最优手段,但他也同样能够带来一些优化效果。

【Unity】 HTFramework框架(四十)Debug的性能监控_List_07

总结,当你有一段比较复杂的靠人眼难以看出优劣的代码时,检测一下他运行时所消耗的时间和空间,是一个不错的优化指南!


标签:监控,test,Unity,GC,ints,Debug,data,HTFramework
From: https://blog.51cto.com/u_15911199/5934127

相关文章

  • 【Unity】 HTFramework框架(四十二)【进阶篇】使用依赖注入(控制反转模式)
    更新日期:2022年1月4日。Github源码:​​​[点我获取源码]​​​Gitee源码:​​[点我获取源码]​​索引​​依赖注入​​​​使用​​​​InjectPath​​​​InjectUI​​​......
  • Unity UGUI图文混排源码(一)
    我从一开始想到的图文混排的概念都是通过文字间的空隙去粘贴一张图片,这样确定图片前面文字的最后一个位置变成了最主要的参数,接下来就给出两种解决方案首先,先发UGUI源码的一......
  • Unity UGUI基础之Text
    Text作为UGUI最基础的控件以及最常用的控件,它在项目中的应用绝对可以算是最多的,任何一个UI界面可以说都离不开它,它的基本属性如下:一、recttransform组件:recttransform(矩形......
  • Unity UGUI实现图文混排
    目前在unity实现图文混排的好像都是通过自定义字体然后在文本获取字符的位置,用图片替换掉图片标签,这样对于支持英文来说,并没有什么影响。然后对于中文来说就是一个相当麻烦......
  • Unity插件 - MeshEditor(十) 模型风力拉扯特效
    更新日期:2020年4月23日。 Github源码:​​​[点我获取源码]​​ 先上几张效果图:  (导演:我们需要一个刮风的效果,道具组,上大风扇) (导演:咔!!!行了,道具组你们明天不用来上班了) ......
  • Unity 纹理优化及TextureProcessor工具
    更新日期:2020年11月18日。Github源码:​​​[点我获取源码]​​索引​​纹理优化​​​​TextureProcessor工具​​​​使用​​​​Resizer纹理缩放器​​​​打开纹理缩放......
  • Unity插件 - MeshEditor(十一) 模型正弦扭曲特效
    更新日期:2020年4月23日。Github源码:​​​[点我获取源码]​​ 先上几张效果图:   OK,进入今天的正题吧,插一个正弦函数的话题进来: 首先,正弦函数曲线,如下:  在如上坐标系......
  • 【Unity】 HTFramework框架(三十九)UI的数据驱动模式,MVVM
    更新日期:2020年10月24日。Github源码:​​​[点我获取源码]​​​Gitee源码:​​[点我获取源码]​​索引​​UI的数据驱动模式​​​​使用​​​​数据模型​​​​Bindabl......
  • Unity UGUI无限列表(Infinite List)
    更新日期:2020年10月16日。Github源码:​​​[点我获取源码]​​索引​​InfiniteList​​​​使用​​​​创建InfiniteListScrollRect​​​​InfiniteListScrollRect参数......
  • Unity - 粒子系统跟随路径移动
    对于最新版的粒子系统ParticleSystem,要让其跟随路径移动,无非就是借用其自身的API直接为每个粒子设置速度。看一下最终的效果图:编辑器为了能在场景中更方便的编辑路径,我们要......