Deep analysis of GC optimization for each value type of XLua under Unity
Foreword
Unity 's C#GC Alloc (abbreviated as gc below ) is a big problem. After embedding a dynamic Lua , the interaction between them is easy to produce gc , and various Luaprograms also regard this as the focus of performance optimization. These optimizations are simply not complicated.
The culprit is here
Take a look at these two functions
1
2
3
4
5
6
7
8
9
int
inc1(
int
i)
{
return
i + 1;
}
object
inc2(
object
o)
{
return
(
int
)o + 1;
}
window measured under inc1 performance is inc2 of 20 times!
Why is the difference so big? The main reason is that in its parameters and return type, the inc2 parameter is an object type, meaning that a value type (such as an integer) needs to be boxing . The specific point is to apply a block of memory on the heap, copy the type information and values into it, and use it. Need unboxing , that is, just copy the stack of memory to the stack, such as the completion of the function is executed, the heap memory is gc detected no reference, release the heap memory.
A 20- fold difference is a return of one parameter. With such parameters increasing, the difference is even greater. And what's worse is that: GC is more difficult to control, Unity 's mobile games project, GC is often the culprit of Caton.
All current lua programs for lua and c # inter interactive gc optimization, value or type of optimization, in fact, are doing one thing: avoid inc2 situation .
1
2
3
4
5
6
7
8
9
| int inc1( int i) { return i + 1; } object inc2( object o) { return ( int )o + 1; } |
C# calls Lua to avoid inc2
Lua is a dynamically typed language, its function can accept any type, any number of parameters, the return value is any type, any number. If you want to access luafunctions with a common interface , the situation will be worse than inc2 : In order to support any number of arbitrary parameters, we may need to use variable parameters; in order to support any type of multi-return value, this interface may need to return an object Array, not an object . So we have two more arrays to allocate and release. The function prototype is roughly as follows:
Object [] Call( params object [] args)
For the above reasons, although most of the programs provide this method (because it is convenient), they are not recommended. Some programs provide GC-less usage, for example ulua if you want to avoid gc , you have to do this:
1
2
3
4
5
6
var func = lua.GetFunction(
"inc"
);
func.BeginPCall();
func.Push(123456);
func.PCall();
int
num = (
int
)func.CheckNumber();
func.EndPCall();
The idea is to expose Lua 's stack operation api , push the parameters one by one, and call one of the return values. The interfaces for pushing and returning values are of a definite type, in other words the interface of inc1 .
The above is only a single parameter, single return value, in most cases the code will be more tedious.
And slua did not find the relevant program.
The core idea of xLua 's solution is: As long as you tell me what parameters to call, I will help you optimize.
1
2
3
4
[CSharpCallLua]
public
delegate
int
Inc(
int
i);
Inc func= luaenv.Global.Get(
"inc"
);
int
num = func(123456);
1, according to your need to declare a delegate , labeled CSharpCallLua ;
2, the implementation of the generated code;
3. Use the Get interface of Table to map the inc function to the func delegate;
4. Next, you can use this delegate happily .
More complex parameters are the same as above: declare, get, use. There is only one more step than the Call interface with gc , which is as simple as using the Callinterface, and it is even simpler to handle return values, and it also brings the benefits of strong type checking.
What if the lua function has multiple return values?
Multiple return values will be mapped to the return value of C# and the output parameters, mapped from left to right.
In addition, xLua also supports a lua table mapping to a C# interface . Access to the interface 's properties will access the corresponding field in the lua table . The member method call will call the corresponding function in the lua table . Similarly, no gc .
How is this done? In other words, it is not complicated. Lua functions map to c# delegate . xLua generates a code for the delegate that declares CSharpCallLua . For example , the generated code of Inc will be similar to this:
1
2
3
4
5
6
7
8
9
10
11
12
13
public
int
SystemInt32(
int
x)
{
//...init
LuaAPI.lua_getref(L, _Reference);
LuaAPI.xlua_pushinteger(L, x);
int
__gen_error = LuaAPI.lua_pcall(L, 1, 1, err_func);
//...error handle
int
__gen_ret = LuaAPI.xlua_tointeger(L, err_func + 1);
LuaAPI.lua_settop(L, err_func - 1);
return
__gen_ret;
}
The delegate returned by the Get method will point to this method. From the code point of view, and ulua no gc code is similar, the difference is that someone's home was handwritten, and because xLua layer of less packaging, direct call Lua 's API , these should also be more efficient.
Object [] Call( params object [] args)
1
2
3
4
5
6
| var func = lua.GetFunction( "inc" ); func.BeginPCall(); func.Push(123456); func.PCall(); int num = ( int )func.CheckNumber(); func.EndPCall(); |
1
2
3
4
| [CSharpCallLua] public delegate int Inc( int i); Inc func= luaenv.Global.Get( "inc" ); int num = func(123456); |
1
2
3
4
5
6
7
8
9
10
11
12
13
| public int SystemInt32( int x) { //...init LuaAPI.lua_getref(L, _Reference); LuaAPI.xlua_pushinteger(L, x); int __gen_error = LuaAPI.lua_pcall(L, 1, 1, err_func); //...error handle int __gen_ret = LuaAPI.xlua_tointeger(L, err_func + 1); LuaAPI.lua_settop(L, err_func - 1); return __gen_ret; } |
Comments
Post a Comment