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

2024-01-15 22:20

本文主要是介绍xlua源码分析(五) struct类型优化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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 = 100print(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))
endlocal 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] endreturn 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) endend,__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.xend},__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
endfunction Vector3.Get(v)		return v.x, v.y, v.z	
end

这篇关于xlua源码分析(五) struct类型优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/610392

相关文章

Python中Json和其他类型相互转换的实现示例

《Python中Json和其他类型相互转换的实现示例》本文介绍了在Python中使用json模块实现json数据与dict、object之间的高效转换,包括loads(),load(),dumps()... 项目中经常会用到json格式转为object对象、dict字典格式等。在此做个记录,方便后续用到该方

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Python实战之SEO优化自动化工具开发指南

《Python实战之SEO优化自动化工具开发指南》在数字化营销时代,搜索引擎优化(SEO)已成为网站获取流量的重要手段,本文将带您使用Python开发一套完整的SEO自动化工具,需要的可以了解下... 目录前言项目概述技术栈选择核心模块实现1. 关键词研究模块2. 网站技术seo检测模块3. 内容优化分析模

Java实现复杂查询优化的7个技巧小结

《Java实现复杂查询优化的7个技巧小结》在Java项目中,复杂查询是开发者面临的“硬骨头”,本文将通过7个实战技巧,结合代码示例和性能对比,手把手教你如何让复杂查询变得优雅,大家可以根据需求进行选择... 目录一、复杂查询的痛点:为何你的代码“又臭又长”1.1冗余变量与中间状态1.2重复查询与性能陷阱1.

Python内存优化的实战技巧分享

《Python内存优化的实战技巧分享》Python作为一门解释型语言,虽然在开发效率上有着显著优势,但在执行效率方面往往被诟病,然而,通过合理的内存优化策略,我们可以让Python程序的运行速度提升3... 目录前言python内存管理机制引用计数机制垃圾回收机制内存泄漏的常见原因1. 循环引用2. 全局变

python中的显式声明类型参数使用方式

《python中的显式声明类型参数使用方式》文章探讨了Python3.10+版本中类型注解的使用,指出FastAPI官方示例强调显式声明参数类型,通过|操作符替代Union/Optional,可提升代... 目录背景python函数显式声明的类型汇总基本类型集合类型Optional and Union(py

MySQL中查询和展示LONGBLOB类型数据的技巧总结

《MySQL中查询和展示LONGBLOB类型数据的技巧总结》在MySQL中LONGBLOB是一种二进制大对象(BLOB)数据类型,用于存储大量的二进制数据,:本文主要介绍MySQL中查询和展示LO... 目录前言1. 查询 LONGBLOB 数据的大小2. 查询并展示 LONGBLOB 数据2.1 转换为十

Python多线程应用中的卡死问题优化方案指南

《Python多线程应用中的卡死问题优化方案指南》在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助... 目录问题描述优化方案1. 网络请求优化2. 多线程架构优化3. 全局异常处理4. 配置管理优化优化效果1.

MySQL中优化CPU使用的详细指南

《MySQL中优化CPU使用的详细指南》优化MySQL的CPU使用可以显著提高数据库的性能和响应时间,本文为大家整理了一些优化CPU使用的方法,大家可以根据需要进行选择... 目录一、优化查询和索引1.1 优化查询语句1.2 创建和优化索引1.3 避免全表扫描二、调整mysql配置参数2.1 调整线程数2.

MyBatis的xml中字符串类型判空与非字符串类型判空处理方式(最新整理)

《MyBatis的xml中字符串类型判空与非字符串类型判空处理方式(最新整理)》本文给大家介绍MyBatis的xml中字符串类型判空与非字符串类型判空处理方式,本文给大家介绍的非常详细,对大家的学习或... 目录完整 Hutool 写法版本对比优化为什么status变成Long?为什么 price 没事?怎