XLua官方demo5-避免c#和lua间值类型的GC分析

  代码里的几个标签解释:

         · XLua.GCOptimize: gc代码优化。对于一个c#纯值类型(官网指一个只包含值类型的struct,可以嵌套其它只包含值类型的struct)或者c#枚举值加上了这个配置,会使得该类型在lua和c#间传递不产生gc alloc,该类型的数组访问也不会产生gc。

         (除枚举之外,包含无参构造函数的复杂类型,都会生成lua table到该类型,以及该类型的一维数组转换代码,可以优化转换性能)

        查了很多资料才理解括号内句子的意思,要理解gz代码优化,首先要知道gc是如何产生的

        网上的经典例子,我觉得很易懂,直接搬过来用了:

int inc1(int i)
{
  return i+1;
}

object inc2(object o)
{
  return (int)o + 1;
}

        对比这两个函数,inc1的性能是inc2的20倍。差距这么大的原因在于参数及其返回类型。inc2的参数是 object,意味着一个值类型传入需要boxing,具体一点就是在堆上申请一块内存,把类型信息,值拷贝进去,要使用的时候需要unboxing,就是从刚刚那堆内存拷贝到栈上,等函数执行完毕后,这个堆内存被gc检测到已经没引用,释放该内存。

        而这个20倍只是一个参数加一个返回的情况,随着参数增多,函数变得复杂,这个差距会更大。而且GC比较难控制,Unity的手游项目,GC往往是卡顿的元凶。对于lua和c#间交互的gc优化,或者值类型优化,实际都是在避免inc2的情况发生。

        XLua的解决方法:你告诉我用什么参数调用,我来优化

[CSharpCallLua]
public delegate int Inc(int i);
public int inc(int p){
return p
}
Inc func = luaenv.Global.Get("inc");
int num = func(666);

       代码分析:  1. 按你的需要声明一个delegate,打上CSharpCallLua标签

                          2. 用table的Get接口把inc函数映射到func委托

                          3. 多复杂的参数都是和上面一样:声明/获取/使用

                          4. 另外XLua还支持一个lua table映射到一个c# interface,对这个interface的属性访问会访问到lua table的对应字段,成员方法会调用到lua table里对应函数,且也是无gc的。对应demo5里的代码:

[CSharpCallLua]
public delegate void ArrayAccess(Array arr);
[CSharpCallLua]
public interface IExchanger{
  void exchange(Array arr);
}
ArrayAccess farr;
IExchanger ie; //接口用来映射lua里的table
luaenv.DoString(@"
  function array_exchange(arr)
    arr[0],arr[1] = arr[1],arr[0]
  end

  exchanger = {
    exchange = function(self,arr)
        array_exchange(arr)
    end
  }
")
luaenv.Global.Get("array_exchange",out farr);luaenv.Global.Get("exchanger",out ie);
//lua调用c#值但无gc
farr(a1);
//c#用interface调lua
ie.exchange(a2);

      XLua复杂值类型优化:

      复杂值产生的问题:每一次值类型放入对象池中就会碰到inc2的情况,会boxing成一个新的对象,伴有入池的一系列操作

      解决:Xlua是这么优化的-->值拷贝。从c#传递到lua本质上是把值拷贝到lua table,避免入池而避免inc2;简单类型也是一个c#的int传入lua,也是直接把int值拷贝到lua的栈上。

      原理[铺垫到这里才真正解释了开篇括号的内容]:

                 1. 生成struct的值拷贝代码,用于把struct里的各字段拷贝到一块非托管内存(Pack),以及从非托管内存拷贝出各字段的值(UnPack)

                 2. c#传struct到lua:调用lua的api,申请一块userdata(对于c#来说是非托管代码),调用pack把struct打包

                 3. lua回传到给c#:调用unpack解出struct

                 4. 实质上就是把c#数据结构序列化到一块内存以及从内存反序列化回来

        优点

                1. 操作简单,声明一下GCOptimize即可,之所以要声明,是避免生成代码过多

                2. 相比table方案更省内存。实测实table方案1/3

    其它值类型GC优化:

      下面大多数优化都只在xLua有效,可以在其05_NoGc示例看到用法,生成代码后运行在profiler看你效果。

  1、枚举类型传递无GC;

    2、decimal不丢失精度而且无GC;

    3、所有无GC的类型,它的数组访问没有GC,这个貌似大多数方案都做到;

  4、能被GCOptimize优化的struct,在Lua可以直接传一个对应结构的table,无GC;

  5、LuaTable提供一系列泛化Get/Set接口,可以传递值类型而无GC;

  6、一个interface加入到CSharpCallLua后,可以用table来实现这个interface,通过这interface访问table无GC;



附一点demo5里的注释:

//设置当前对象的lua脚本
luaenv.Global.Set("monoBehaviour",this);
//获取lua中的全局方法映射到delegate中
luaenv.Global.Get("id",out f1);
//f1 = luaenv.Global.Get<IntParam>("id");
luaenv.Global.Get("id",out f2);
//f2 = luaenv.Global.Get<Vector3Param>("id");
//c#类型加了这个配置,xLua会生成这个类型的适配代码(包括构造该类型实例,成员属性、方法等等),不然就会用性能较低的反射方式访问
[LuaCallCSharp]
//如果希望把一个lua函数适配到一个c# delegate(c#侧是各种回掉:UI事件,delegate参数),或者把一个lua table适配到一个c# interface,需要加上该配置
[CSharpCallLua]


  

 

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值