Insight Profiling in lua

想要是用 Unreal Insight Profile lua 端的调用,可以把 SCOPED_NAMED_EVENT 封装到 Lua 端,但是因为 Lua 没有 C++ 的 RAII 机制,无法实时地检测离开作用域时机,所以只能通过在起始和结尾位置添加标记:

1
2
3
4
5
function TimerMgr:UpdateTimer(DeltaTime)
local profile_tag = UE4.FProfileTag("TimerMgr_UpdateTimer")
-- dosomething...
profile_tag:End()
end

在 C++ 端的实现:

ProfileTag.h
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
#pragma once

#include "CoreMinimal.h"
#include "ProfileTag.generated.h"

USTRUCT()
struct FProfileTag
{
GENERATED_BODY()
FProfileTag(){}

void Begin(const FString& Name)
{
if(bInit) return;
StaticBegin(Name);
bInit = true;
}
void End()
{
if(bInit)
{
StaticEnd();
bInit = false;
}
}

~FProfileTag()
{
End();
}

static void StaticBegin(const FString& Name)
{
#if PLATFORM_IMPLEMENTS_BeginNamedEventStatic
FPlatformMisc::BeginNamedEventStatic(FColor::Yellow, *Name);
#else
FPlatformMisc::BeginNamedEvent(FColor::Yellow, *Name);
#endif
}

static void StaticEnd()
{
FPlatformMisc::EndNamedEvent();
}
bool bInit = false;
};

暴露给 Lua 端:

Lualib_ProfileTag.cpp
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
66
67
68
69
70
71
72
73
74
75
#include "UnLuaEx.h"
#include "LuaCore.h"
#include "ProfileTag.h"
static int32 FProfileTag_New(lua_State *L)
{
int32 NumParams = lua_gettop(L);
if (NumParams < 1)
{
UE_LOG(LogUnLua, Log, TEXT("%s: Invalid parameters!"), ANSI_TO_TCHAR(__FUNCTION__));
return 0;
}

void *Userdata = NewTypedUserdata(L, FProfileTag);
FProfileTag *V = new(Userdata) FProfileTag();
if (NumParams > 1)
{
FString Name = ANSI_TO_TCHAR((char*)lua_tostring(L,2));
V->Begin(Name);
}
return 1;
}

static int32 FProfileTag_Begin(lua_State *L)
{
int32 NumParams = lua_gettop(L);
if (NumParams < 1)
{
UE_LOG(LogUnLua, Log, TEXT("%s: Invalid parameters!"), ANSI_TO_TCHAR(__FUNCTION__));
return 0;
}

FProfileTag *V = (FProfileTag*)GetCppInstanceFast(L, 1);
if (!V)
{
UE_LOG(LogUnLua, Log, TEXT("%s: Invalid FProfileTag!"), ANSI_TO_TCHAR(__FUNCTION__));
return 0;
}
FString Name = ANSI_TO_TCHAR((char*)lua_tostring(L,2));
V->Begin(Name);
return 0;
}
static int32 FProfileTag_End(lua_State *L)
{
int32 NumParams = lua_gettop(L);
if (NumParams < 1)
{
UE_LOG(LogUnLua, Log, TEXT("%s: Invalid parameters!"), ANSI_TO_TCHAR(__FUNCTION__));
return 0;
}

FProfileTag *V = (FProfileTag*)GetCppInstanceFast(L, 1);
if (!V)
{
UE_LOG(LogUnLua, Log, TEXT("%s: Invalid FProfileTag!"), ANSI_TO_TCHAR(__FUNCTION__));
return 0;
}

V->End();
return 0;
}

static const luaL_Reg FProfileTagLib[] =
{
{"__call",FProfileTag_New},
{ "Begin", FProfileTag_Begin },
{ "End", FProfileTag_End },
{ nullptr, nullptr }
};

BEGIN_EXPORT_REFLECTED_CLASS(FProfileTag)
ADD_STATIC_FUNCTION_EX("StaticBegin",void,StaticBegin,const FString&)
ADD_STATIC_FUNCTION_EX("StaticEnd",void,StaticEnd)
ADD_LIB(FProfileTagLib)
END_EXPORT_CLASS()
IMPLEMENT_EXPORTED_CLASS(FProfileTag)

然后即可在 Lua 中使用,并且能够在 Unreal Insight 中查看 Lua 端代码执行的消耗:

提供了三种使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- using 1
function TimerMgr:UpdateTimer(DeltaTime)
local profile_tag = UE4.FProfileTag("TimerMgr_UpdateTimer")
-- dosomething...
profile_tag:End()
end

-- using 2
function TimerMgr:UpdateTimer(DeltaTime)
local profile_tag = UE4.FProfileTag()
profile_tag:Begin(("TimerMgr_UpdateTimer"))
-- dosomething...
profile_tag:End()
end

-- using 3
function TimerMgr:UpdateTimer(DeltaTime)
UE4.FProfileTag.StaticBegin(("TimerMgr_UpdateTimer"))
-- dosomething...
UE4.FProfileTag.StaticEnd()
end

建议使用创建对象的方式使用,因为创建的对象,如果漏掉 End,在 lua 对象被销毁时也会调用 End,不会造成 Insight 的数据采集问题,使用 StaticBegin/StaticEnd 则一定要保证所有的代码分支能执行到 StaticEnd
可以组合 lua 的 debug.getinfo(1).name 获取函数名字在 Insight 中显示:

1
2
3
4
5
function TimerMgr:UpdateTimer(DeltaTime)
local profile_tag = UE4.FProfileTag(debug.getinfo(1).name)
-- dosomething...
profile_tag:End()
end