前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用UE4/UE5的stat监控Lua的性能

使用UE4/UE5的stat监控Lua的性能

作者头像
quabqi
发布2021-11-04 10:56:31
2.2K1
发布2021-11-04 10:56:31
举报
文章被收录于专栏:Dissecting UnrealDissecting Unreal

stat是虚幻引擎提供的性能统计和优化工具,比较类似于Unity的Profiler,通过在代码中埋指定的函数或宏,就可以将需要的信息监控起来。前面会简单介绍在C++中怎么用,以及stat实现原理,后面会介绍怎样在Lua中使用。如果对stat很了解了,只想看Lua使用方法,可以翻到最后。顺便说一下,Lua的使用方法是我自己实现的,目前网上都没有类似教程或做法,各种第三方Lua插件或其他语言的支持插件都没有对应的支持,所以我觉得比较有参考价值。

stat下面就简单讲一点点。网上的资料,包括知乎上都有非常多教程,也可以参考官方文档:

UE本身已经在引擎中埋好了非常多的点,什么都不加,在游戏控制台中只要输入stat startfile,stat stopfile,就可以记录下来游戏在start和stop之间的性能数据,包括每个监控代码块的耗时,内存,各种计数等信息。

记录信息保存在Saved/Profiling文件夹下,以ue4stats作为后缀的文件(UE5是uestats,很睿智的做法,毕竟大版本升级了,这里去掉了4,后缀不同所以不兼容老版本,但其实本质没什么变化)

通过SessionFrontend打开

如果只想看统计信息,不想开引擎,也可以到引擎目录下找UnrealFrontend.exe独立程序打开看。

我们项目基本上都是测试同学抓stat数据,开发进行性能分析。因为平时我也在写代码,写一半了测试发来stat文件要分析,这时因为刚写一半的引擎或游戏代码还没编译或编译不过,肯定开不了引擎,那么就没法从引擎里面打开SessionFrontend了。但这时直接用这个独立程序看,就可以不重新编译,写代码和分析性能两件事都不耽误,还是很方便的。

比如这里能看到游戏线程以及内部每个加了统计的代码块平均耗时,或者单帧耗时等。具体看官方文档,这里不细说。

在自己的代码中使用

在自己的代码中使用,UE也提供了教程,写的很详细

比如PlayerController Tick

在代码中用宏来声明:

使用的时候也有对应的宏

具体原理

这里是CycleCounter,也就是统计每帧被这个宏包含的代码块耗时的。可以看到用起来非常简单。我们查看源码:

可以看到,就是构造的时候Start,析构的时候Stop,因此在函数局部代码块里定义一个这个类的对象,当出了作用域就会析构,自然的就记录的开始和结束的时间。

Start和Stop都是调用FThreadStats的AddMessage函数,发了两个事件。

进去看,里面构造Message的时候,就从PlatformTime上取了Cycles()得到开始和结束的时间,证明了猜测的正确。

当然除了耗时外,也可以统计其他信息,比如自定义整数,浮点数,字符串等。他们都是通过FThreadStats::AddMessage发送给stat线程的,stat线程收到后就会展示到屏幕或者写文件。这里的stat信息,其实最终都构造成了一个很长的FName字符串,每一段信息都有固定前缀,拼接到一起成为一个LongName,大概如下面这样(这里只说明原理,可以自行查看源码)

所以,stat文件里,记录的就是很多条这样的信息。

至于stat创建,必须在代码里某个位置,用宏来声明。可以看到,其实就是定义了一个static变量,一般都是在全局范围的。

如果你比较细心,就会发现stat全部都是用宏来定义的,依赖于C++的静态编译,把需要统计的stat定义以及对应代码,通过编译推导,导出给引擎,那么对于lua这样的动态语言,比如想统计lua中某个函数的耗时,因为lua是解释执行的,并不能在编译期知道lua代码中定义的stat信息。因此C++提供的这些宏,在Lua中就完全用不了了,即使硬着头皮先定义好,但在lua中统计性能还得让C++不停的编译,就变成了一个非常麻烦的事情,也失去了使用Lua来高效开发的意义。所以我接下来做的事情,就是封装一套接口,可以让stat在运行时定义以及统计,这样Lua就可以方便的使用stat来统计性能了。

在运行时定义和使用stat

首先,做这件事,我们就要清楚Stat到底做了什么。前面也说了,统计时本质就是AddMessage到stat线程,但是AddMessage需要StatId这样的参数,需要先构造。C++是通过全局对象构造的,而运行时构造StatId的是要解决的问题,接下来会说。但总之,只要搞定了在运行时构造stat id和调用stat统计这两件事,就达到了目的。

构造stat

只要你一层一层的扒开stat中封装的宏,最终你会看到,这个statid是通过DoSetup这个函数,如下图这样构造出来的,显然这个函数即使运行时调用也没什么不可以。至于怎么调用到这里的具体过程就不细说了,可以自行看源码。

所以,这段代码就是我要封装的构造stat函数,把各个参数都暴露出来

这里简单讲一下具体每个参数的意义:

StatName,就是统计的Stat程序内部用的名字,StatDesc是一个友好可读的名字。

就像上图,我们在宏里写的一样,对应这两个参数

然后下面的Group,因为我这里只是统计Lua,所以就直接在C++预定义好了,所有Lua都使用同一个Group。这里就不通过参数传进来了

接下来是比较重要的4个参数:

bool bShouldClearEveryFrame:是否每帧都清除,如果为true,这里面的值每帧就清理成0了

EStatDataType::Type InStatType:stat数据类型,有int64,double,FName几种

bool bCycleStat:这个是CycleCounter专用,CycleCounter这个要打开

FPlatformMemory::EMemoryCounterRegion MemRegion:这个是内存专用

直接用这个函数非常丑,所以我就包装了几个和stat宏差不多的:

对于统计来说,lua不像CycleCounter有构造函数和析构函数,那么我们就只能给lua中封装两个函数Start和Stop,让lua手动调用(也很方便,比较像Unity的Profiler用法),当然除了CycleCounter外,还有其他的stat,比如加值,减值,设值等,本质上都是AddMessage给stat线程,这个封装就很简单了,我也照着stat宏封装了一些方便的函数

最后,lua要使用,肯定要包装成lua的格式导出到Lua

这样,就可以在lua中愉快的使用stat了

最后,附上具体源码,我是用的UnLua,其他Lua做法类似,可以自行修改使用。

github:

补充一点,对于Lua5.4,可以使用close语法来做到和析构函数一样得效果,有需要可以自行实现

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 在自己的代码中使用
  • 具体原理
  • 在运行时定义和使用stat
  • 构造stat
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档