【Unity/XLua】xlua自带教程示例分析(五)—— 优化GC


在Xlua中,使用[GCOptimize]可以避免C#和Lua间传递值类型(普通值类型,只包含值类型的strcut或嵌套struct,枚举类型,以及它们组成的数组)时发生gc alloc

lua脚本

function id(...)
    return ...
end

function add(a, b) return a + b end

function array_exchange(arr)
    arr[0], arr[1] = arr[1], arr[0]
end

local v3 = CS.UnityEngine.Vector3(7, 8, 9)
local vt = CS.XLuaTest.MyStruct(5, 6)

function lua_access_csharp()
    monoBehaviour:FloatParamMethod(123) --primitive
    monoBehaviour:Vector3ParamMethod(v3) --vector3
    local rnd = math.random(1, 100)
    local r = monoBehaviour:Vector3ParamMethod({x = 1, y = 2, z = rnd}) --vector3
    assert(r.x == 1 and r.y == 2 and r.z == rnd)
    monoBehaviour:StructParamMethod(vt) --custom struct
    r = monoBehaviour:StructParamMethod({a = 1, b = rnd, e = {c = rnd}})
    assert(r.b == rnd and r.e.c == rnd)
    monoBehaviour:EnumParamMethod(CS.XLuaTest.MyEnum.E2) --enum
    monoBehaviour:DecimalParamMethod(monoBehaviour.a5[0])
    monoBehaviour.a1[0], monoBehaviour.a1[1] = monoBehaviour.a1[1], monoBehaviour.a1[0] -- field
end

exchanger = {
    exchange = function(self, arr)
        array_exchange(arr)
    end
}

A = { B = { C = 789}}
GDATA = 1234;

解释

这段代码是Lua脚本的示例,结合了与C#交互的一些功能。让我们逐个解释:

  1. 函数 id(...)add(a, b):

    • id(...) 函数是一个不定参数的函数,它返回接收到的所有参数。
    • add(a, b) 函数简单地计算两个参数的和并返回结果。该

    这两个函数的目的是要测试让数据从C#传入lua再传回C#,观察gc情况


  2. 函数 array_exchange(arr):

    • 这个函数交换了数组 arr 的第一个和第二个元素的位置。Lua中的数组索引从1开始,因此 arr[0] 实际上是 arr[1]arr[1]arr[2]

    该函数利用了lua的等号特性,本质是一个swap函数,并且只交换arr的头两个数据


  3. 定义了两个变量 v3vt:

    • v3 是一个使用C#的Unity引擎的 Vector3 结构体,表示一个三维向量 (7, 8, 9)。
    • vt 是一个自定义的结构体 MyStruct 的实例,其中包含字段 (5, 6)。

  4. 函数 lua_access_csharp():

    • 这个函数演示了如何在Lua中访问和调用C#中的方法和字段:
      • monoBehaviour:FloatParamMethod(123) 调用了一个接受单精度浮点数参数的方法。
      • monoBehaviour:Vector3ParamMethod(v3) 传递了一个 Vector3 类型的参数给一个接受 Vector3 的方法。
      • 使用 math.random(1, 100) 生成一个随机数 rnd,然后传递给一个接受 Vector3 参数的方法,并验证返回的结果。
      • monoBehaviour:StructParamMethod(vt) 传递了一个自定义结构体类型的参数。
      • 类似地,传递一个包含字段的表给一个接受结构体参数的方法,并验证返回结果。
      • monoBehaviour:EnumParamMethod(CS.XLuaTest.MyEnum.E2) 传递了一个枚举类型的参数。
      • monoBehaviour:DecimalParamMethod(monoBehaviour.a5[0]) 传递了一个十进制数参数。
      • 最后一行交换了一个数组 monoBehaviour.a1 的两个元素的位置。

    monoBehaviour是在C#中set的变量,即this,所以这里是在使用C#函数处理lua对象


  5. 变量 exchanger:

    • exchanger 是一个包含 exchange 方法的表,该方法接受一个数组参数并调用 array_exchange 函数来交换数组的两个元素。

    该表在C#中创建有对应函数的接口获取,通过C#的接口调用其方法


  6. 全局变量 AGDATA:

    • A 是一个嵌套表结构,其中包含 B 字段,B 字段包含 C 字段。
    • GDATA 是一个全局变量,存储整数值 1234。

​ 这两个变量会在C#中每次取出,并且 + 1放回


C#调用过程

[GCOptimize]
[LuaCallCSharp]
public struct Pedding
{
    public byte c;
}

[GCOptimize]
[LuaCallCSharp]
public struct MyStruct
{
    public int a;
    public int b;
    public decimal c;
    public Pedding e;
}

[LuaCallCSharp]
public enum MyEnum
{
    E1,
    E2
}

使用GCOptimize优化只有值类型的Struct以及嵌套Struct

使用LuaCallCSharp指示Lua可以调用这些Struct


[CSharpCallLua]
public delegate int IntParam(int p);

[CSharpCallLua]
public delegate Vector3 Vector3Param(Vector3 p);

[CSharpCallLua]
public delegate MyStruct CustomValueTypeParam(MyStruct p);

[CSharpCallLua]
public delegate MyEnum EnumParam(MyEnum p);

[CSharpCallLua]
public delegate decimal DecimalParam(decimal p);

[CSharpCallLua]
public delegate void ArrayAccess(Array arr);

[CSharpCallLua]
public interface IExchanger
{
    void exchange(Array arr);
}

创建一系列delegate接收Lua函数,以及一个接口接收lua的表



以上都是类外的前期声明准备,接下来是在类中的字段定义

首先最重要的一点是,类要加上LuaCallCSharp的标签,因为后面会在里面向Lua传入自己的this指针,因此lua是可以调用类的

在类中,先定义一些等会要用到的妙妙工具(字段)

IntParam f1;
Vector3Param f2;
CustomValueTypeParam f3;
EnumParam f4;
DecimalParam f5;

ArrayAccess farr;
Action flua;
IExchanger ie;
LuaFunction add;

[NonSerialized]
public double[] a1 = new double[] { 1, 2 };
[NonSerialized]
public Vector3[] a2 = new Vector3[] { new Vector3(1, 2, 3), new Vector3(4, 5, 6) };
[NonSerialized]
public MyStruct[] a3 = new MyStruct[] { new MyStruct(1, 2), new MyStruct(3, 4) };
[NonSerialized]
public MyEnum[] a4 = new MyEnum[] { MyEnum.E1, MyEnum.E2 };
[NonSerialized]
public decimal[] a5 = new decimal[] { 1.00001M, 2.00002M };

前五个委托接收Lua的id函数,不同的值类型数据都测了一遍(从C#传lua再传回来)

farr接收lua的交换函数,传入数组,交换数组头两个元素,再传回来

flua接收lua_access_csharp()函数,负责触发lua去访问c#的变量和函数

ie传入表exchanger,并通过里面的函数去访问交换函数(同2)

add是用LuaFunction对象创建的,可以通过add.Func<int, int, int>(34, 56); // LuaFunction.Func<T1, T2, TResult> 的方式访问。

后面的a1 ~ a5则是为交换函数准备的不同类型的数组


luaenv.DoString(script);

luaenv.Global.Set("monoBehaviour", this);

luaenv.Global.Get("id", out f1);
luaenv.Global.Get("id", out f2);
luaenv.Global.Get("id", out f3);
luaenv.Global.Get("id", out f4);
luaenv.Global.Get("id", out f5);

luaenv.Global.Get("array_exchange", out farr);
luaenv.Global.Get("lua_access_csharp", out flua);
luaenv.Global.Get("exchanger", out ie);
luaenv.Global.Get("add", out add);

luaenv.Global.Set("g_int", 123);
luaenv.Global.Set(123, 456);
int i;
luaenv.Global.Get("g_int", out i);
Debug.Log("g_int:" + i);
luaenv.Global.Get(123, out i);
Debug.Log("123:" + i);

接下来先在luaenv中运行脚本,再将对应的值提取到C#字段中,具体对应关系上一段已经说了


// c# call lua function with value type but no gc (using delegate)
//仅仅传递数值再返回C#中
f1(1); // primitive type
f2(new Vector3(1, 2, 3)); // vector3
MyStruct mystruct1 = new MyStruct(5, 6);
f3(mystruct1); // custom complex value type
f4(MyEnum.E1); //enum
decimal dec1 = -32132143143100109.00010001010M;
f5(dec1); //decimal

// using LuaFunction.Func<T1, T2, TResult>
//使用LuaFunction风格的函数触发
add.Func<int, int, int>(34, 56); // LuaFunction.Func<T1, T2, TResult>

// lua access c# value type array no gc
//传递数组进去并交换头两个数的位置再传回来
farr(a1); //primitive value type array
farr(a2); //vector3 array
farr(a3); //custom struct array
farr(a4); //enum arry
farr(a5); //decimal arry

// lua call c# no gc with value type
//让lua侧调用C#变量
flua();

//c# call lua using interface
//使用接口风格调用表中的交换数组头两个元素的函数
ie.exchange(a2);

//no gc LuaTable use
//直接设置获取lua中的值的数
luaenv.Global.Set("g_int", 456);
int i;
luaenv.Global.Get("g_int", out i);

luaenv.Global.Set(123.0001, mystruct1);
MyStruct mystruct2;
luaenv.Global.Get(123.0001, out mystruct2);

decimal dec2 = 0.0000001M;
luaenv.Global.Set((byte)12, dec1);
luaenv.Global.Get((byte)12, out dec2);

//这两个是lua中定义的值和嵌套表,访问和修改其也不会产生gc
int gdata = luaenv.Global.Get<int>("GDATA");
luaenv.Global.SetInPath("GDATA", gdata + 1);

int abc = luaenv.Global.GetInPath<int>("A.B.C");
luaenv.Global.SetInPath("A.B.C", abc + 1);

//lua手动gc函数
luaenv.Tick();

这一段代码是Update中的,会每帧执行,主要是展示大量传递数据下不会产生gc alloc

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值