xlua源码分析(五) struct类型优化

news2024/12/26 14:41:40

xlua源码分析(五) struct类型优化

上一节我们分析了xlua是如何实现lua层访问C#值类型的,其中我们重点提到了xlua默认实现方式下,struct访问的效率问题。实际上,xlua还提供了两种优化的方式,可以大大提高struct访问的性能。具体例子在Examples 12_ReImplementInLua中。

第一种优化方式就是在lua层改造C#的struct,C# struct push到lua层时仍为userdata,但它的metatable不指向C#层struct,而是lua层自己实现的:

function test_vector3(title, v1, v2)
    print(title)
    v1.x = 100
    print(v1.x, v1.y, v1.z)
    print(v1, v2)
    print(v1 + v2)
    v1:Set(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z)
    print(v1)
    print(CS.UnityEngine.Vector3.Normalize(v1))
end

local get_x, set_x = xlua.genaccessor(0, 8)
local get_y, set_y = xlua.genaccessor(4, 8)
local get_z, set_z = xlua.genaccessor(8, 8)

local fields_getters = {
    x = get_x, y = get_y, z = get_z
}
local fields_setters = {
    x = set_x, y = set_y, z = set_z
}

local ins_methods = {
    Set = function(o, x, y, z)
        set_x(o, x)
        set_y(o, y)
        set_z(o, z)
    end
}

local mt = {
    __index = function(o, k)
        --print('__index', k)
        if ins_methods[k] then return ins_methods[k] end
        return fields_getters[k] and fields_getters[k](o)
    end,

    __newindex = function(o, k, v)
        if fields_setters[k] then fields_setters[k](o, v) else error('no such field ' .. k) end
    end,

    __tostring = function(o)
        return string.format('vector3 { %f, %f, %f}', o.x, o.y, o.z)
    end,

    __add = function(a, b)
        return CS.UnityEngine.Vector3(a.x + b.x, a.y + b.y, a.z + b.z)
    end
}

xlua.setmetatable(CS.UnityEngine.Vector3, mt)
test_vector3('----after change metatable----', CS.UnityEngine.Vector3(1, 2, 3), CS.UnityEngine.Vector3(7, 8, 9))

这里的代码,就是在lua层实现了一下Vector3的get/set属性和方法,然后替换掉原先的metatable,xlua.setmetatable就是做这个工作的,替换的逻辑很简单,就是找到要替换类的type id,重新设置到registry表里:

public static int XLuaMetatableOperation(RealStatePtr L)
{
    try
    {
        ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
        Type type = getType(L, translator, 1);
        if (type == null)
        {
            return LuaAPI.luaL_error(L, "xlua.metatable_operation, can not find c# type");
        }

        bool is_first = false;
        int type_id = translator.getTypeId(L, type, out is_first);

        var param_num = LuaAPI.lua_gettop(L);

        if (param_num == 1) //get
        {
            LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, type_id);
            return 1;
        }
        else if (param_num == 2) //set
        {
            if (LuaAPI.lua_type(L, 2) != LuaTypes.LUA_TTABLE)
            {
                return LuaAPI.luaL_error(L, "argument #2 must be a table");
            }
            LuaAPI.lua_pushnumber(L, type_id);
            LuaAPI.xlua_rawseti(L, 2, 1);
            LuaAPI.xlua_rawseti(L, LuaIndexes.LUA_REGISTRYINDEX, type_id);
            return 0;
        }
        else
        {
            return LuaAPI.luaL_error(L, "invalid argument num for xlua.metatable_operation: " + param_num);
        }
    }
    catch (Exception e)
    {
        return LuaAPI.luaL_error(L, "c# exception in xlua.metatable_operation: " + e);
    }
}

不过,lua层的Vector3依旧是userdata,如何在lua层对userdata设置/获取数据呢?为此,xlua提供了xlua.genaccessor函数,它接受两个参数,第一个参数表示要设置/获取的字段相对于struct的内存偏移,第二个参数表示要设置/获取的字段类型,对于Vector3,x,y,z的偏移分别为0,4,8,而它们的类型均为float,float在xlua预先定义的类型ID为8:

#define T_INT8   0
#define T_UINT8  1
#define T_INT16  2
#define T_UINT16 3
#define T_INT32  4
#define T_UINT32 5
#define T_INT64  6
#define T_UINT64 7
#define T_FLOAT  8
#define T_DOUBLE 9

genaccessor函数是在C层实现的,那其实很简单了,就是把userdata作为要访问内存的首地址,加上偏移量offset,执行memcpy即可,如果是get,就是从userdata拷贝到value,再push到lua栈;如果是set,就先从lua栈上取出value,再拷贝到userdata。

#define DIRECT_ACCESS(type, push_func, to_func) \
int xlua_struct_get_##type(lua_State *L) {\
	CSharpStruct *css = (CSharpStruct *)lua_touserdata(L, 1);\
	int offset = xlua_tointeger(L, lua_upvalueindex(1));\
	type val;\
	if (css == NULL || css->fake_id != -1 || css->len < offset + sizeof(type)) {\
		return luaL_error(L, "invalid c# struct!");\
	} else {\
		memcpy(&val, (&(css->data[0]) + offset), sizeof(type));\
		push_func(L, val);\
		return 1;\
	}\
}\
\
int xlua_struct_set_##type(lua_State *L) { \
	CSharpStruct *css = (CSharpStruct *)lua_touserdata(L, 1);\
	int offset = xlua_tointeger(L, lua_upvalueindex(1));\
	type val;\
	if (css == NULL || css->fake_id != -1 || css->len < offset + sizeof(type)) {\
		return luaL_error(L, "invalid c# struct!");\
	} else {\
	    val = (type)to_func(L, 2);\
		memcpy((&(css->data[0]) + offset), &val, sizeof(type));\
		return 0;\
	}\
}\

上面例子的运行结果如下:

xlua源码分析(五) struct类型优化1

第二种优化方式,是将struct映射成table,即C#层push到lua层的struct,不再为userdata,而是一个table,xlua提供了PackAsTable这个attribute指示生成代码时采用映射table的方式:

[GCOptimize(OptimizeFlag.PackAsTable)]
public struct PushAsTableStruct
{
    public int x;
    public int y;
}

然后,lua层也需要实现配套的代码,即struct的object metatable和class metatable,相当于在lua层实现struct:

local mt = {
    __index = {
        SwapXY = function(o) --成员函数
            o.x, o.y = o.y, o.x
        end
    },

    __tostring = function(o) --打印格式化函数
        return string.format('struct { %d, %d}', o.x, o.y)
    end,
}

xlua.setmetatable(CS.XLuaTest.PushAsTableStruct, mt)

local PushAsTableStruct = {
    Print = function(o) --静态函数
        print(o.x, o.y)
    end
}

setmetatable(PushAsTableStruct, {
    __call = function(_, x, y) --构造函数
        return setmetatable({x = x, y = y}, mt)
    end
})

xlua.setclass(CS.XLuaTest, 'PushAsTableStruct', PushAsTableStruct)

在测试代码中,我们先在C#层push一下struct:

PushAsTableStruct test;
test.x = 100;
test.y = 200;
luaenv.Global.Set("from_cs", test);

然后再在lua层进行测试:

print('--------------from csharp---------------------')
assert(type(from_cs) == 'table')
print(from_cs)
CS.XLuaTest.PushAsTableStruct.Print(from_cs)
from_cs:SwapXY()
print(from_cs)

print('--------------from lua---------------------')
local from_lua = CS.XLuaTest.PushAsTableStruct(4, 5)
assert(type(from_lua) == 'table')
print(from_lua)
CS.XLuaTest.PushAsTableStruct.Print(from_lua)
from_lua:SwapXY()
print(from_lua)

此时C#层push时,不会再生成userdata,而是生成一个table,然后设置字段x和字段y:

public void PushXLuaTestPushAsTableStruct(RealStatePtr L, XLuaTest.PushAsTableStruct val)
{
    if (XLuaTestPushAsTableStruct_TypeID == -1)
    {
        bool is_first;
        XLuaTestPushAsTableStruct_TypeID = getTypeId(L, typeof(XLuaTest.PushAsTableStruct), out is_first);
        
    }
    
    
    LuaAPI.xlua_pushcstable(L, 2, XLuaTestPushAsTableStruct_TypeID);
    
    LuaAPI.xlua_pushasciistring(L, "x");
    LuaAPI.xlua_pushinteger(L, val.x);
    LuaAPI.lua_rawset(L, -3);
    
    LuaAPI.xlua_pushasciistring(L, "y");
    LuaAPI.xlua_pushinteger(L, val.y);
    LuaAPI.lua_rawset(L, -3);
    
    
}

同样的道理,要从lua层把struct传递到C#层,就要获取lua层的table,把它的字段x和字段y取出,依次赋值到C#对象上:

public static void UnPack(ObjectTranslator translator, RealStatePtr L, int idx, out XLuaTest.PushAsTableStruct val)
{
    val = new XLuaTest.PushAsTableStruct();
    int top = LuaAPI.lua_gettop(L);
    
    if (Utils.LoadField(L, idx, "x"))
    {
        
        translator.Get(L, top + 1, out val.x);
        
    }
    LuaAPI.lua_pop(L, 1);
    
    if (Utils.LoadField(L, idx, "y"))
    {
        
        translator.Get(L, top + 1, out val.y);
        
    }
    LuaAPI.lua_pop(L, 1);
    
}

例子的输出结果如下:

xlua源码分析(五) struct类型优化2

这两种优化方式,各有优劣,第一种方式,userdata比table更加省内存;而第二种方式,使用原始table操作性能上要比使用userdata要好。两种方式都需要额外生成一些代码。与tolua相比,tolua的struct是采用了类似第二种的方式,tolua的struct在lua层就是个table,需要完整按照C#层实现一遍struct。而数据传输的逻辑,稍微不太相同,tolua是使用lua函数进行数据传输,例如Vector3,tolua可以通过一个get函数直接返回3个float*给C#层,也可以通过一个new函数直接使用x,y,z三个参数构造出一个lua层的struct,pack和unpack的逻辑都放在了lua层里。

function Vector3.New(x, y, z)				
	local t = {x = x or 0, y = y or 0, z = z or 0}
	setmetatable(t, Vector3)						
	return t
end

function Vector3.Get(v)		
	return v.x, v.y, v.z	
end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1386911.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件测试|如何使用Selenium处理隐藏元素

简介 我们在使用selenium进行web自动化测试时&#xff0c;有时候会遇到元素被隐藏&#xff0c;从而无法对元素进行操作&#xff0c;导致我们的用例报错的情况。当我们遇到元素被隐藏的情况时&#xff0c;需要先对隐藏的元素进行处理&#xff0c;才能继续进行我们的操作&#x…

一篇文章搞懂Jenkins持续集成解决的是什么问题

01 持续集成的定义 大师 Martin Fowler 是这样定义持续集成的: 持续集成是一种软件开发实战, 即团队开发成员经常集成他们的工作. 通常, 每个成员每天至少集成一次, 也就意味着每天可能发生多次集成. 持续集成并不能消除Bug, 而是让它们非常容易发现和改正. 根据对项目实战的…

第08章_面向对象编程(高级)拓展练习(关键字:static,代码块,关键字:final,抽象类和抽象方法,接口,内部类,枚举类,注解,包装类)

文章目录 第08章_面向对象编程&#xff08;高级&#xff09;拓展练习01-关键字&#xff1a;static1、银行账户类2、图形类3、数组工具类4、二分查找5、二分查找6、素数7、阅读代码&#xff0c;分析运行结果8、阅读代码&#xff0c;分析运行结果 02-代码块9、阅读代码&#xff0…

软件测试|如何使用selenium处理下拉框?

简介 下拉框是网页表单中常见的元素之一&#xff0c;通常用于选择不同的选项。对于我们的自动化测试工作来说&#xff0c;操作下拉框是我们经常需要处理的元素&#xff0c;selenium作为我们最常使用的web自动化测试框架&#xff0c;也是支持我们对下拉框进行操作的。本文我们就…

Github镜像加速器-FastGit

简介 FastGit 是一个对于 GitHub.com 的镜像加速器。使用共享资源为 GitHub 加速。 FastGit中文指南 # 基本使用 关于 FastGit 的使用&#xff0c;本质上与 git 有关。常规的面向 GitHub 的 clone 命令可能如下&#xff1a; git clone https://github.com/author/repo使用 F…

Qt 使用vs2019制作Qt静态库( *.lib )并使用

一 .创建静态库 1.创建Qt Class Library(Qt静态类库)项目 2.设置项目名以及项目路径(注意:不能有中文字符) 点击next 3.选则需要的模式以及Qt 模块 然后点击next,Finish完成创建 4. 然后手动添加Qt Widget Form File (.ui)并对设计ui 5. tpendialog.h #pragma once #includ…

VScode远程连接开发嵌入式开发板

在做嵌入式开发时&#xff0c;很多时候需要远程连接或者远程调试设备&#xff0c;这时可以通过VScode上的插件来很方便的进行远程连接和调试。 ssh远程连接嵌入式开发板&#xff1a; 1、安装vscode ssh远程插件&#xff1a;Remote-SSH。 2、点击""&#xff0c;输入…

排序算法之七:归并排序(非递归)

1.非递归实现思路 我们之前学习了递归实现的归并排序&#xff0c;是分治的思想&#xff0c;即先分解&#xff0c;再归并 这篇文章我们讲一下非递归的实现 非递归实现的思路是模拟递归的过程&#xff0c;在递归过程中&#xff0c;我们找key将数组分成左右数组&#xff0c;然后…

Peter算法小课堂—树上建模

太戈编程1720题 题目描述&#xff1a; 传说有一个大家族里共n名男性成员&#xff0c;编号1到n。其中共有n-1条父子关系。现在他们要挑选若干人组成家族护卫队抵抗外族入侵。i号成员的战斗力为z[i], 大家当然希望挑选最强护卫队。但是为了防止“父子矛盾”的魔咒应验&#xff…

【算法】如何不用中间变量交换两个数据?

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法分析与设计知识专栏&#xff1a;算法分析&#x1f525; 给大家跳…

Radzen Blazor Studio 脚手架框架解读

背景 组织管理管理准备使用Blazor这个工具实现&#xff0c;因为其有对应的 scaffold 脚手架&#xff0c;先构建数据库&#xff0c;然后通过向导&#xff0c;生成CRUD以及对应的接口&#xff0c;那么有必要看一下&#xff0c;其内部的代码结构是什么样的。 结构 接口层 有两类…

【RTOS】快速体验FreeRTOS所有常用API(2)任务管理

目录 二、任务管理2.1 任务创建&#xff08;三种方式&#xff09;1&#xff09;动态内存分配方式创建任务2&#xff09;静态内存分配方式创建任务3&#xff09;带有任务参数方式创建任务 2.2 任务删除2.3 两种delay 二、任务管理 该部分在上份代码基础上修改得来&#xff0c;代…

t2vec code

文章目录 数据预处理执行过程训练执行过程preprocess.jl 解释h5 文件结构 数据预处理执行过程 (base) zzqserver1:~/project/t2vec/preprocessing$ julia porto2h5.jl Processing 1710660 trips… 100000 200000 300000 400000 500000 600000 700000 800000 900000 1000000 11…

Halcon轮廓的处理

Halcon轮廓的处理 文章目录 Halcon轮廓的处理1. 轮廓分割2. 轮廓的筛选3. 轮廓的连接4. 轮廓的拟合 输出了目标的轮廓后&#xff0c;接下来还需要对轮廓进行处理&#xff0c;这主要基于以下3个原因。 &#xff08;1&#xff09;对于某些测量任务而言&#xff0c;并不需要分析目…

ArkTS中自定义组件

ArkTS中自定义组件 一、组件位置二、Hello.ets自定义组件自定义组件 三、Second.ets父组件 一、组件位置 一个项目下所有的自定义的组件名不可以重复&#xff0c;无论是否在一个ets文件中 二、Hello.ets自定义组件 自定义组件 1&#xff1a;组件必须使用Component装饰 2&#…

最新智能AI系统ChatGPT网站程序源码+详细图文搭建部署教程,Midjourney绘画,GPT语音对话+ChatFile文档对话总结+DALL-E3文生图

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

Spring Boot接口请求响应慢,超过10秒以上,如无法优化SQL或代码的情况下,建议写入数据库或缓存中,请求接口时从数据库或缓存中读取返回

举例 Override public Map<String, Object> getCockpitStaffAttendanceTask() {Map<String, Object> map new HashMap<>();int chuqin 0; //出勤int queqin 0; //缺勤int chidao 0; //迟到int zaotui 0; //早退//获取所有设备卡号 并且已经绑定了人Lis…

Redis之bigkey

目录 1、什么是bigkey&#xff1f; 2、bigkey大的小 3、bigkey有哪些危害&#xff1f; 4、bigkey如何产生&#xff1f; 5、bigkey如何发现&#xff1f; 6、bigkey如何删除&#xff1f; 7、BigKey调优&#xff0c;惰性释放lazyfree 8、生产上限制keys * /flushdb/flushal…

python爬虫之线程与多进程知识点记录

一、线程 1、概念 线程 在一个进程的内部&#xff0c;要同时干多件事&#xff0c;就需要同时运行多个“子任务”&#xff0c;我们把进程内的这些“子任务”叫做线程 是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际运作单位。一条线程指…