找回密码
 立即注册
查看: 227|回复: 0

xLua源码解析——值类型传递无GC

[复制链接]
发表于 2022-11-19 10:43 | 显示全部楼层 |阅读模式
概述:

xLua的FAQ里:
值类型传递会有gc alloc么?
如果你使用的是delegate调用lua函数,或者用LuaTable、LuaFunction的无gc接口,或者数组的话,以下值类型都是没gc的:
1、所有的基本值类型(所有整数,所有浮点数,decimal);
2、所有的枚举类型;
3、字段只包含值类型的struct,可嵌套其它只包含值类型struct;
其中2、3需要把该类型加到GCOptimize。
xLua示例Examples->05_NoGc->NoGc.cs展示怎么去避免值类型的GC。下面分析一下,为什么会产生GC,以及xLua如何解决这个问题。
为什么会产生GC

通常来说,只要堆分配了内存,也就是实例化引用对象,在对象使用完时,就会被GC。所以值类型时从栈上分配的,原则上不会产生GC,但是在lua和C#交互过程中,值传递会因为装箱拆箱产生GC。
如何解决

以MyStruct为例:
[GCOptimize]
[LuaCallCSharp]
public struct MyStruct
{
    public MyStruct(int p1, int p2)
    {
        a = p1;
        b = p2;
        c = p2;
        e.c = (byte)p1;
    }
    public int a;
    public int b;
    public decimal c;
    public Pedding e;
}
看下面的MyStruct的部分生成代码(打上了GCOptimize标签)。
// XLuaTest.MyStruct生成代码
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
static int __CreateInstance(RealStatePtr L)
{
    try
    {
        ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
        if (LuaAPI.lua_gettop(L) == 3 && LuaTypes.LUA_TNUMBER == LuaAPI.lua_type(L, 2) && LuaTypes.LUA_TNUMBER == LuaAPI.lua_type(L, 3))
        {
            int _p1 = LuaAPI.xlua_tointeger(L, 2);
            int _p2 = LuaAPI.xlua_tointeger(L, 3);

            var gen_ret = new XLuaTest.MyStruct(_p1, _p2);
            // 有[GCOptimize]标签
            // translator.Push(L, gen_ret);
            // 无[GCOptimize]标签
            translator.PushXLuaTestMyStruct(L, gen_ret);

            return 1;
        }

        if (LuaAPI.lua_gettop(L) == 1)
        {
            // 有[GCOptimize]标签
            // translator.Push(L, default(XLuaTest.MyStruct));
            // 无[GCOptimize]标签
            translator.PushXLuaTestMyStruct(L, default(XLuaTest.MyStruct));
            return 1;
        }
    }
    catch (System.Exception gen_e)
    {
        return LuaAPI.luaL_error(L, "c# exception:" + gen_e);
    }
    return LuaAPI.luaL_error(L, "invalid arguments to XLuaTest.MyStruct constructor!");
}
如果没有打[GCOptimize] 标签,则translator.PushXLuaTestMyStruct(RealStatePtr L, XLuaTest.MyStruct val)替换为translator.Push(RealStatePtr L, object o)。
可以看出,通用的Push方法的参数是object,这里就会触发装箱的操作。
所以打上[GCOptimize]后,生成的代码主要就是多了Push、Get、Update三个函数的专用函数。相关代码如下:
int XLuaTestMyStruct_TypeID = -1;
public void PushXLuaTestMyStruct(RealStatePtr L, XLuaTest.MyStruct val)
{
    if (XLuaTestMyStruct_TypeID == -1)
    {
        bool is_first;
        XLuaTestMyStruct_TypeID = getTypeId(L, typeof(XLuaTest.MyStruct), out is_first);

    }

    IntPtr buff = LuaAPI.xlua_pushstruct(L, 25, XLuaTestMyStruct_TypeID);
    if (!CopyByValue.Pack(buff, 0, val))
    {
        throw new Exception("pack fail fail for XLuaTest.MyStruct ,value=" + val);
    }

}

public void Get(RealStatePtr L, int index, out XLuaTest.MyStruct val)
{
    LuaTypes type = LuaAPI.lua_type(L, index);
    if (type == LuaTypes.LUA_TUSERDATA)
    {
        if (LuaAPI.xlua_gettypeid(L, index) != XLuaTestMyStruct_TypeID)
        {
            throw new Exception("invalid userdata for XLuaTest.MyStruct");
        }

        IntPtr buff = LuaAPI.lua_touserdata(L, index); if (!CopyByValue.UnPack(buff, 0, out val))
        {
            throw new Exception("unpack fail for XLuaTest.MyStruct");
        }
    }
    else if (type == LuaTypes.LUA_TTABLE)
    {
        CopyByValue.UnPack(this, L, index, out val);
    }
    else
    {
        val = (XLuaTest.MyStruct)objectCasters.GetCaster(typeof(XLuaTest.MyStruct))(L, index, null);
    }
}

public void UpdateXLuaTestMyStruct(RealStatePtr L, int index, XLuaTest.MyStruct val)
{

    if (LuaAPI.lua_type(L, index) == LuaTypes.LUA_TUSERDATA)
    {
        if (LuaAPI.xlua_gettypeid(L, index) != XLuaTestMyStruct_TypeID)
        {
            throw new Exception("invalid userdata for XLuaTest.MyStruct");
        }

        IntPtr buff = LuaAPI.lua_touserdata(L, index);
        if (!CopyByValue.Pack(buff, 0, val))
        {
            throw new Exception("pack fail for XLuaTest.MyStruct ,value=" + val);
        }
    }

    else
    {
        throw new Exception("try to update a data with lua type:" + LuaAPI.lua_type(L, index));
    }
}
还有Pack和UnPack的方法:
public static void UnPack(ObjectTranslator translator, RealStatePtr L, int idx, out XLuaTest.MyStruct val)
{
    val = new XLuaTest.MyStruct();
    int top = LuaAPI.lua_gettop(L);

    if (Utils.LoadField(L, idx, "a"))
    {

        translator.Get(L, top + 1, out val.a);

    }
    LuaAPI.lua_pop(L, 1);

    if (Utils.LoadField(L, idx, "b"))
    {

        translator.Get(L, top + 1, out val.b);

    }
    LuaAPI.lua_pop(L, 1);

    if (Utils.LoadField(L, idx, "c"))
    {

        translator.Get(L, top + 1, out val.c);

    }
    LuaAPI.lua_pop(L, 1);

    if (Utils.LoadField(L, idx, "e"))
    {

        translator.Get(L, top + 1, out val.e);

    }
    LuaAPI.lua_pop(L, 1);

}

public static bool Pack(IntPtr buff, int offset, XLuaTest.MyStruct field)
{

    if (!Pack(buff, offset, field.a))
    {
        return false;
    }

    if (!Pack(buff, offset + 4, field.b))
    {
        return false;
    }

    if (!Pack(buff, offset + 8, field.c))
    {
        return false;
    }

    if (!Pack(buff, offset + 24, field.e))
    {
        return false;
    }

    return true;
}
public static bool UnPack(IntPtr buff, int offset, out XLuaTest.MyStruct field)
{
    field = default(XLuaTest.MyStruct);

    if (!UnPack(buff, offset, out field.a))
    {
        return false;
    }

    if (!UnPack(buff, offset + 4, out field.b))
    {
        return false;
    }

    if (!UnPack(buff, offset + 8, out field.c))
    {
        return false;
    }

    if (!UnPack(buff, offset + 24, out field.e))
    {
        return false;
    }

    return true;
}

// 额外贴出具体的Pack方法
public static bool Pack(IntPtr buff, int offset, int field)
{
     return LuaAPI.xlua_pack_int32_t(buff, offset, field);
}
总结

上面只举例了struct,其他LuaTable和LuaFunction的无GC方法等,也是同样的原理,通过生成专门的转换方法,避免使用通用方法的objcet类型参数引起的装拆箱。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 20:12 , Processed in 0.086472 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表