Skip to content

inkiu0/ELuaProfiler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ELuaProfiler

☁️ Easy Lua Profile ☁️

Update

2023.04.19 优化统计性能

优化ELuaProfiler统计性能,约提高到300%


2023.02.20 PerFrame模式支持柱状图


2023.02.07 支持序列化和反序列化

UE4 Console输入: eluaprofiler start 0/1 eluaprofiler stop

elprof文件会存放在Saved/ELuaProfiler/

后续优化: 流式保存PerFrame模式下的elprof文件


Supported Solutions

Solution Supported
unlua-ue4
slua-ue4 ×
slua-unity ×

Build

UE4

  1. configure Setup.bat/Setup.command
    • set src=E:\repo\ELuaProfiler\UE4\UnLua\
      set dst=E:\YourProject\Plugins\
      
  2. Run Setup.bat/Setup.command
  3. Build YourProject

Unity

待开发

Usage

ELuaMonitor

ELuaMonitor主要用于剖析Lua的CPU性能,以及内存频繁开辟引起GC的问题,编辑器界面如图:

MonitorMode

右上角为模式选择控件,目前支持三种模式

  1. PerFrame
    • 逐帧采样,并且可以按帧数前后回溯
  2. Total
    • 累计采样,统计一段时间内的开销情况,最常用。
  3. Statistics
    • 统计模式,在Total的基础上将所有节点平铺开,统计单函数的开销。

MonitorController

  1. Play
    • 中间的按钮为Play,点击Play后,如果LuaVM启动了,会立刻开始Profile。否则等待LuaVM启动,自动开始Profiler
    • 开始Profile后按钮变为暂停键,可以暂停和恢复Profile
  2. Clear
    • 最右侧为Clear按钮,点击Clear后会立刻停止Profile,并清空当前Profile数据
  3. NextFrame/PrevFrame
    • 逐帧回溯,分别为上一帧和下一帧。
  4. CurFrameIdx/TotalFrame
    • 当前帧数/总帧数
    • CurFrameIdx支持手动输入
    • 当0 < CurFrameIdx < TotalFrame,会停在CurFrameIdx这一帧
    • 否则CurFrameIdx会等于TotalFrame,并跟随TotalFrame一起增长

MonitorData

  1. TotalTIme(ms)
    • 函数从Call到Return总共消耗的时间,单位为毫秒
    • 该值会受Profiler本身开销影响,主要是Profiler取Time的开销(因为Profiler取Time必须是同步的,但比如自增函数的开销比取Time开销还小,势必会造成误差,可以控制ProfileDepth来消除)
  2. TotalTime(%)
    • 该函数调用时间开销占父节点的百分比
    • TotalTime / Parent.TotalTime
  3. SelfTime(ms)
    • 代表该函数自身的开销
    • TotalTime减去各子节点的TotalTime
  4. SelfTime(%)
    • 自身开销占自身节点的比重
    • SelfTime / SelfTime
  5. Average(ms)
    • 平均每次调用耗时
    • TotalTime / Calls
  6. Alloc(kb)
    • 函数调用期间开辟的内存,单位为kb
  7. Alloc(%)
    • 该函数开辟的内存占父节点的比重
    • Alloc / Parent.Alloc
  8. GC(kb)
    • 函数调用期间释放的内存,因为Lua的GC为步进式,所以不能准确形容当前函数释放的内存。
    • 但一级节点的Alloc - GC可以作为整个Lua的内存增量,可以用来观察代码是否有明显泄露,但具体定位还应该使用ELuaMemAnalyzer。
  9. GC(%)
    • 该函数调用期间发生的GC占父节点的比重
    • GC / Parent.GC
  10. Calls
    • 该函数被调用次数

Max Depth

Max Depth控制Profile的最大深度,最小值为1,最大值为1000(可在代码中更改),Max Depth可以有效消除Profiler的误差。从我的经验来说,Profiler的误差主要来源于Profiler的GetTime函数。而且GetTime必须是同步进行的,所以这部分误差会一直存在。考虑以下代码

function EmptyFunction()

end

function Counting()
    for i = 1, 1000 do
        EmptyFunction()
    end
end

当我们统计到Counting函数的时候,会统计到1000次EmptyFunction函数的开销。但由于EmptyFunction函数的开销过小,甚至比Profiler的GetTime函数的开销还小。所以Counting的统计势必存在很大的误差,这个时候我们可以将MaxDepth设定在Counting这一层,不再继续展开,不统计过细的分支(在这个例子中指EmptyFunction),我们就能正确地观察到Counting的性能开销。

一般我们在实战中,从1开始慢慢增加Depth,直到我们停在一个合适的地方。

排序

默认按照TotalTime降序排列,点击不同的Title会根据不同的数据进行降序排列。 目前支持以下数据的降序排列:

  1. TotalTime
  2. SelfTime
  3. Average
  4. AllocSize
  5. GCSize
  6. Count

ELuaMemAnalyzer

Snapshot

在ELuaMemAnalyzer中点击一次采样,即可生成一个SnapshotSnapshot中包含了当前时刻的内存情况,以_G为根节点。 同时Snapshot也支持逻辑运算,以方便剖析内存的泄露和增长。

Snapshot Logic Operation

Snapshot为单元进行逻辑运算,目前支持&与|或^异或运算,这三个运算配合强制GC可以很好地查找泄露。选择了一个Snapshot后,点击需要进行的操作按钮(AND、OR、XOR),然后再点击另一个Snapshot。就会对两个Snapshot进行逻辑运算,并生成一个新的Snapshot

  1. & Operation
    • 两颗Snapshot进行与运算,得到两个时刻相同的内存部分。
  2. | Operation
    • 两颗Snapshot进行或运算,得到两个时刻总的内存部分,相当于A + B
  3. ^ Operation
    • 两颗Snapshot进行异或预算,得到两个时刻的内存差异部分。
  4. Memory Leak
    • 在刚启动游戏时,或强制GC后,采样得到SnapshotA
    • 在游戏内进行游玩时,每隔5分钟采样,得到SnapshotBSnapshotCSnapshotD
    • 常驻内存 = SnapshotA & SnapshotB & SnapshotC & SnapshotD

Deviation

由于种种原因,内存统计的误差都不可避免。误差主要来自于这几方面:

  1. Lua的Intern机制
    • 由于Lua对短字符串和number采用了Intern机制,所以这部分存在重复统计。
    • 后续优化考虑做一个去除。
  2. 多重引用,比如A:{B: {D, E}, C: {D, F}},这样一颗三层的树。
    • 为了保证单独查看节点B和节点C的正确性,B = D + EC = D + F
    • 所以A = D + E + D + F,存在一定的误差。

Roadmap

1. Support slua-ue4

接入slua-ue4

2. Remote Profile

将ELuaProfiler分为Server和Client,支持真机远程Profile

3. Serialize & Deserialize

支持Profile数据的序列化和反序列化 2023.02.07实现

4. Support slua-unity

接入slua-unity,并编写Unity版本的EditorUI

5. Support unlua-unity

接入unlua-unity

6. Support xlua-unity

接入xulua-unity