Lua Profiler——快速定位Lua性能问题

导读

随着Lua在项目中的大量使用,它所带来的性能问题也逐步成为了项目运行时的重大性能瓶颈之一。特别是内存相关的性能问题,无论是内存分配过大还是内存泄露无法回收,目前都已经在不少研发项目中集中爆发。

UWA推出的GOT Online中的Lua模式已经慢慢成为研发团队对Lua进行日常性能监控的有效手段。因此,也有越来越多的团队反馈,在监控到table数持续上涨,引用Mono对象持续增多等等问题时,应该如何快速地解决?

本次博物纳新推荐的开源库项目:LuaProfiler-For-Unity,相信可以帮助到大家。Lua Profiler For Unity支持XLua、SLua、ToLua,该工具是基于远程Socket的Profiler工具,因此它支持Android,iOS的真机Profiler。

小编将结合实际项目中遇到的问题,为大家介绍这款开源库的用法。

开源库链接:https://lab.uwa4d.com/lab/5bf38db072745c25a80c1276

作者Blog:https://www.zhihu.com/people/ElPsyConGree/activities


操作流程

一、部署和安装
参考项目中的Readme文档阐述的详细流程:
1、打开两个Unity项目,一个放进游戏客户端,一个用于展示数据
2、打开LuaProfiler文件夹


LuaProfiler文件目录

3、将 LuaProfilerClient 文件夹复制到游戏项目内,如果您的C#Lua脚本位于Plugins文件夹中,则将 LuaProfilerClient 复制到插件。此工具必须确保该代码必须位于具有C#Lua代码的同一DLL中。
4、使用 Unity5.6 or newer version Unity版本将 LuaProfilerServer 作为Unity项目打开
5、如果Unity版本低于5,请在开始游戏前调用以下代码。

MikuLuaProfiler.HookLuaSetup.OnStartGame();

注意:不要在static变量声明里面启动Lua流程(比如XLua的Demo),请在Awake或者Start里面进行调用。

二、使用教程
(该小节内容全部来自于项目Readme文档,想要阅读更详细教程的读者可访问项目主页:https://lab.uwa4d.com/lab/5bf38db072745c25a80c1276

1、配置客户端


Lua Profiler Client界面

LuaProfilerClient插件所在的游戏项目工程,通过Editor界面的Window->Lua Profiler Window打开客户端设置界面。选择想要分析器的代码类型,C#代码颜色为绿色,Lua代码颜色为蓝色。

2、配置服务器端
LuaProfilerServer插件所在的项目工程,通过Editor界面的Window->Lua Profiler Window打开服务器数据显示界面。


Lua Profiler Server界面

单击OpenService,等待客户端连接。


操作流程示意图

3、相关功能操作


使用示意图

3.1 相关基础数据统计

折线走势图展示了PSS、Mono内存、Lua内存、FPS的走势:


折线走势图
数据列表

数据列表中列举了当前帧下图所示的相关数据:

3.2 监控注册表


注册表引用的Lua对象统计

此处区域会显示当前被注册表引用的类型为Function和table的Lua对象。

3.3 Diff两个不同时期的Lua变量
选取一个适当的时机,比如配置表加载完后,准备打开一个新的UI的时候点击MarkLuaRecord按钮。


操作步骤

打开UI然后关闭并卸载掉UI资源,点击DiffRecord,工具将会对Mark时候的Lua变量与DiffRecord时候的变量进行差异比较。


数据显示界面

点击ShowLog按钮,将会把文件存盘打开之后将把对于变量的类型以及引用路径打印出来。注意_G表示全局表,_R表示被C#引用的对象。


引用链

3.4 Destroy null values统计

该模块展示了Unity已经将资源释放,而Lua仍然引用的变量。


引用链


功能原理详解

一、相关基础数据统计
查看这部分数据时,除了关注排序中开销较大、内存占用较高的函数外还可以关注一些重要的字段,例如:

require字段,比较普遍的性能问题在于:加载配置表时产生一个内存占用较大的table,可以通过搜索require字段并进行排序查找内存占用较大的配置表,并对它进行针对性优化。

关于配置表的优化方案可以阅读《Lua配置表存储优化方案》

[Lua]字段,按照Lua内存的self进行排序,可以定位GC比较严重的Lua函数,进行针对性优化。也可以查看调用次数等。


在搜索框中搜索[Lua]

善用搜索功能,会揪出很多意想不到的性能问题,比如:老生常谈的Vertex3。

二、监控注册表
以SLua框架的示例Circle.cs 为例:

//Circle.cs
public class Circle : MonoBehaviour {

  LuaSvr svr;
    LuaSvr s2;
    LuaTable self;
    LuaFunction update;

    [CustomLuaClass]
    public delegate void UpdateDelegate(object self);

    UpdateDelegate ud;

  void Start () {
    svr = new LuaSvr();
        svr.init(null, () =>
        {
            self = (LuaTable)svr.start("circle/circle");
            update = (LuaFunction)self["update"];
            ud = update.cast<UpdateDelegate>();
        }
        );
        ......
//Circle.lua
local class={}
function main()
  local slider = GameObject.Find("Canvas/Slider"):GetComponent(UI.Slider)
  local counttxt = GameObject.Find("Canvas/Count"):GetComponent(UI.Text)
  slider.onValueChanged:AddListener(
    function(v)
      class:init(v)
      counttxt.text=string.format("cube:%d",v)
    end
  )

  class.root = GameObject("root")
  class.ftext = GameObject.Find("Canvas/Text"):GetComponent(UI.Text)
  class.r=10
  class.cubes={}
  class.t=0
  class.f=0
  class.framet=0
  class.max=0

  class:init()
  return class
end
function class:update() -- gc alloc is zero0
    ......

C# 层创建的变量self、svr、update等,引用了Lua层的class、update等。

使用该工具得到的引用关系如图所示:

其中“@circle/circle&line:15"的函数为:

function(v)
  class:init(v)
  counttxt.text=string.format("cube:%d",v)
end

被添加到UI组件Slider的事件监听中。

“@circle/circle&line:65"的函数为:function class:update(),被一个LuaFunction类型对象update引用。

其中具有key为:bgCurrent、init、root、t等值的table,为Lua代码中的:

local class={}

(其余被引用的table和function是框架初始化时产生的)

C#层是一个ref,内存占用较小,但是Lua层会是一个较为复杂的table或者函数调用等,内存占用较大。当不再使用的C#对象没有被完全释放时,由于C#层内存占用较小,并不会及时进行GC,使得Lua层仍然存在引用,无法进行GC,导致大量内存滞留。

三、Destroy null values统计
在任意一种Lua插件中,都存在类似的机制:在C#层维护一个Cache来引用那些被Lua访问过的C#层对象,防止出现以下的问题:当Lua中再次访问该C#对象时,该对象可能已经被C#层的GC回收掉了,从而导致逻辑错误。所以,在Lua中始终保留某个C#层对象的引用,将会导致其无法被释放,当这样的引用越来越多,就会导致C#层的内存泄漏。

其中比较常见的例子便是:应该被申明为local的对象忘记写local。

function main()
    cube = GameObject.CreatePrimitive(PrimitiveType.Cube)
  ......

然后切换场景,是用工具检测得到被引用对象为:Cube,如图:

具体引用链为:

当切换场景时,虽然场景中没有了Cube对象,但对象池中还有,导致仍然有引用而无法GC。此时Cube对象是一个作为UnityEngine.Object为空,而作为System.Object不为空的对象,原因就是Lua对其的引用不为空,会导致泄漏。解决方案也较为简单,将Cube变量申明为local局部变量,解除引用即可:

function main()
  local cube = GameObject.CreatePrimitive(PrimitiveType.Cube)
  ...... 

更多实战例子可以阅读:https://zhuanlan.zhihu.com/p/89912209

四、Diff两个不同时期的Lua变量
对两个时期的Lua State做两次完整的快照,通过比较两次快照的数据,可以得到相关增加与减少的变量。得到疑似泄露的地方。通过快照获取到的引用链定位泄漏的变量。

该工具得到引用链中:_G表示全局表,_R表示被C#引用的对象


引用链

想要更加深入了解该工具、深入了解Lua性能优化方案的读者,推荐阅读:

1、作者书写的工具介绍与性能检测思路的文章,其中详细解释了Lua、Mono双GC系统、以及Mono对象、Lua对象、Unity对象三者的释放流程以及Lua、C#、C++整个调用流程结构:https://www.zhihu.com/question/307064711

2、UWA Blog中对于Lua性能优化的文章:
《Lua性能优化—Lua内存优化》
《Lua的CPU开销性能优化》

快用UWA Lab合辑Mark好项目!

今天的推荐就到这儿啦,或者它可直接使用,或者它需要您的润色,或者它启发了您的思路......

请不要吝啬您的点赞和转发,让我们知道我们在做对的事。当然如果您可以留言给出宝贵的意见,我们会越做越好。

原文地址:https://www.cnblogs.com/uwatechnologies/p/12617956.html

时间: 2024-10-03 20:18:27

Lua Profiler——快速定位Lua性能问题的相关文章

快速定位网站性能问题,提前下班!

大家好,我是小雨小雨,致力于分享有趣的.实用的技术文章. 内容分为翻译和原创,如果有问题,欢迎随时评论或私信,希望和大家一起进步. 分享不易,希望能够得到大家的支持和关注. 查看network时间 直接来一张大图你怕不怕?哈哈 咱们先看看谷歌浏览器network中waterfall各字段的含义哈.简单看下就成,用到了再查不耽误的. Queueing: 排队时间,比如出现以下几种情况的时候,将进入排队 当前请求前有优先级更高的其他请求 HTTP的1.0和1.1版本中,如果对一个域发送超过六个请求,

90%的人会遇到性能问题,如何用1行代码快速定位?

简介:如何在众多异常性能指标中,找出最核心的那一个,进而定位性能瓶颈点,最后进行性能调优.整篇文章会按照代码.CPU.内存.网络.磁盘等方向进行组织,针对对某一各优化点,会有系统的「套路」总结,便于思路的迁移实践. 1. 代码相关 遇到性能问题,首先应该做的是检查否与业务代码相关——不是通过阅读代码解决问题,而是通过日志或代码,排除掉一些与业务代码相关的低级错误.性能优化的最佳位置,是应用内部. 譬如,查看业务日志,检查日志内容里是否有大量的报错产生,应用层.框架层的一些性能问题,大多数都能从日

快速掌握Lua 5.3 —— 字符串库 (3)

Q:什么情况下"pattern"会匹配空串? A:要小心的使用*和-,因为它们可以匹配零次. -- 如果你打算用"%a*"匹配单词,你会发现到处都是单词. print(string.find(";$% **#$hello13", "%a*")) --> 1 0 print(string.find(";$% **#$hello13", "%a*", 6)) --> 6 5 --

快速定位隐蔽的sql性能问题及调优【转载】

在前几天,有个开发同事问我一个问题,其实也算是技术救援,他说在有个job数据处理的频率比较高,在测试环境中很难定位出在哪有问题,而且速度也还能接 受,但是在生产环境中总是会慢一些,希望我能在测试环境中协助他们,看看是不是sql语句出什么问题了还是其它相关的问题. 这种类似实时监控的语句,从第一印象来说,很可能通过awr捕获不到,如果通过ash来捕获,因为测试环境中有几十套测试环境在运行,就算得到某个时间点 的一些sql语句,直接在报告中映射到语句对应的schema信息还是有一些困难的.因为测试时

快速掌握Lua 5.3 —— 编写提供给Lua使用的C库函数的技巧 (2)

Q:什么是"registry"? A:有时候,我们需要在程序中使用一些非局部的变量.在C中我们可以使用全局变量或是静态变量来实现,而在为Lua编写C库的过程中,使用以上类型的变量并不是一个好的方式.首先,这些变量中无法存储Lua的值.其次,这些变量如果在多个Lua状态机中被使用,则很可能造成非预期的结果. 一个替代方案是,将这些值存储在Lua的全局变量中.这种方式解决了上面提到的两个问题,Lua全局变量可以存储任何Lua的值,同时每一个Lua状态机都有自己独立的一套全局变量.但这依旧不

快速掌握Lua 5.3 —— 资源管理

Q:Lua的"finalizer"? A:在我们之前看到的使用"userdata"的例子中,我们只关心如何创建并使用"userdata",从未关心何时以及如何释放我们创建的"userdata",因为这些事都由Lua的垃圾回收器帮我们处理.然而很多时候,程序并不会这么简单,有可能在其中还会涉及到文件句柄,窗口句柄等,此时这些资源就需要创建者进行管理. 一些面向对象语言提供了析够器用来帮助用户管理这些资源,Lua同样提供了类似的机

Linux性能优化从入门到实战:06 CPU篇:快速定位CPU瓶颈

CPU性能指标 ?? ??(1)CPU使用率:1) 用户态CPU使用率(包括用户态 user 和低优先级用户态 nice).2) 系统CPU使用率.3) 等待 I/O 的CPU使用率.4) 软中断和硬中断的CPU使用率.5) 虚拟机占用的CPU使用率. ??(2)平均负载 Load Average:过去 1 分钟.过去 5 分钟和过去 15 分钟的平均负载 ??(3)进程上下文切换:1) 无法获取资源而导致的自愿上下文切换:2) 被系统强制调度导致的非自愿上下文切换. ??(4)CPU缓存命中率

如何利用快照( snapshot )功能快速定位性能问题

我们常常会遇到这样的困惑,收到用户或者客服的反馈,平台使用有问题,但是测试人员搭建环境后又没办法复现故障,最后导致问题没法解决,眼睁睁地看着用户流失. 这是因为线上生产环境非常复杂.很多时候是偶发性 bug ,但却很难捕捉.特别是随着微服务盛行,系统复杂度增加,线上故障的快速定位和及时分析解决面临着巨大挑战,以前只能靠人来解决.但是人的问题解决能力与速度依赖于经验,有时候甚至需要跨部门的配合,这样的成本非常高,一旦关键人员流失.部门配合不融洽,整个故障的解决速度就会极速下降. 这时候我要给大家带

linux下编译make文件报错“/bin/bash^M: 坏的解释器,使用grep快速定位代码位置

一.linux下编译make文件报错"/bin/bash^M: 坏的解释器 参考文章:http://blog.csdn.net/liuqiyao_01/article/details/41542101#comments 自己测试的结果: [1]使用windows 下的编辑工具 新建文件doc2unix.sh #!/usr/bin/env bash # test PID=$(ps -aef | grep nginx | grep -v grep | grep master |awk '{print