首页 > 编程语言 >Lua与C#交互原理

Lua与C#交互原理

时间:2024-10-21 16:01:25浏览次数:1  
标签:c# lua C# float transform Lua 字符串 交互 id

c#调用lua, 是c#通过Pinvoke(Platform Invoke (平台调用))方式调用了lua的底层的C代码,然后这个执行了lua脚本。

如果一个C#方法要被Lua调用,则首先要将其注册到Lua虚拟机中。

如果C#要调用Lua中的函数,则

  • 首先要在Lua虚拟机中加载该函数(LuaState.DoFile)。
  • 拿到目标函数(LuaState.GetFunction)。  
  • 执行目标函数(LuaFunction.Call)

======================================================================

C#与lua底层都是C/C++

故c#跟lua数据交互也是通过lua虚拟栈,进行压栈、出栈来传递的。

所以一次调用就需要执行很多指令,性能会随着调用次数的频繁,函数参数的增多而变差。

举例

Lua中使用gameobj.transform.position = pos

短短一行代码,其实执行了很多东西,这行代码调用过的关键步骤如下

第一步:

GameObjectWrap.get_transform      lua想从gameobj拿到transform,对应gameobj.transform

LuaDLL.luanet_rawnetobj                把lua中的gameobj变成c#可以辨认的id

ObjectTranslator.TryGetValue          用这个id,从ObjectTranslator中获取c#的gameobject对象

gameobject.transform                     准备这么多,这里终于真正执行c#获取gameobject.transform了

ObjectTranslator.AddObject             给transform分配一个id,这个id会在lua中用来代表这个transform, transform要保存到ObjectTranslator供未来查找

LuaDLL.luanet_newudata                   在lua分配一个userdata,把id存进去,用来表示即将返回给lua的transform

LuaDLL.lua_setmetatable                  给这个userdata附上metatable,让你可以transform.position这样使用它

LuaDLL.lua_pushvalue                      返回transform,后面做些收尾

LuaDLL.lua_rawseti

LuaDLL.lua_remove

第二步:

TransformWrap.set_position              lua想把pos设置到transform.position

LuaDLL.luanet_rawnetobj                  把lua中的transform变成c#可以辨认的id

ObjectTranslator.TryGetValue            用这个id,从ObjectTranslator中获取c#的tr

就这么一行代码,竟然做了这么一大堆的事情!如果是c++,a.b.c =

在这里,频繁的取值、入栈、c#到lua的类型转换,每一步都是满满的cpu时间

优化方案

在C#层中实现该静态方法

static void SetPos(GameObject obj, float x, float y, float z){obj.transform.position = new Vector3(x, y, z);

这样就可以减少使用成员方法导出

省掉了transform的频繁返回,而且还避免了transform经常临时返回引起lua的gc

lua和c#之间传参、返回时,尽可能不要传递以下类型:

严重类: Vector3/Quaternion等unity值类型,数组 , string

次严重类:bool各种object

建议传递:int float double

lua和c#的传参,但是从传参这个角度讲,lua和c#中间其实还夹着一层c。lua、c、c#由于在很多数据类型的表示以及内存分配策略都不同,因此这些数据在三者间传递,往往需要进行转换(术语parameter mashalling),这个转换消耗根据不同的类型会有很大的不同。

严重类,基本上是尝试lua对象与c#对象对应时的瓶颈所致。

严重类中的bool string类型,涉及到c和c#的交互性能消耗,根据微软官方文档,在数据类型的处理上,c#定义了Blittable Types和Non-Blittable Types,其中bool和string属于Non-Blittable Types,意思是他们在c和c#中的内存表示不一样,意味着从c传递到c#时需要进行类型转换,降低性能,而string还要考虑内存分配(将string的内存复制到托管堆,以及utf8和utf16互转)。

考虑在lua中只使用自己管理的id,而不直接引用c#的object

避免lua引用c#

object带来的各种性能问题的其中一个方法就是自己分配id去索引object,同时相关c#导出函数不再传递object做参数,而是传递int。

这带来几个好处:

  • 函数调用的性能更好;
  • 明确地管理这些object的生命周期,避免让lua自动管理这些对象的引用,如果在lua中错误地引用了这些对象会导 致对象无法释放,从而内存泄露;
  • c# object返回到lua中,如果lua没有引用,又会很容易马上gc,并且删除ObjectTranslator对object的引用。自行管理这个引用关系,就不会频繁发生这样的gc行为和分配行为;

故 之前的LuaUtil.SetPos(GameObject obj, float x, float y, float z) 可以进一步优化为

LuaUtil.SetPos(int objID, float x, float y, float z)。

然后我们在自己的代码里头记录objID跟GameObject的对应关系,如果可以,用数组来记录而不是dictionary,则会有更快的查找效率。如此下来可以进一步省掉lua调用c#的时间,并且对象的管理也会更高效

然后用几个不同的调用方式来设置transform的position

方式1:gameobject.transform.position = Vector3.New(1,2,3)

方式2:gameobject:SetPos(Vector3.New(1,2,3))

方式3:gameobject:SetPos2(1,2,3)

方式4:GOUtil.SetPos(gameobject, Vector3.New(1,2,3))

方式5:GOUtil.SetPos2(gameobjectid, Vector3.New(1,2,3))

方式6:GOUtil.SetPos3(gameobjectid, 1,2,3)

分别进行1000000次,结果如下

以下是几个对比测试,你可以复制代码到你的编辑器中,进行测试

a = os.clock()
for i = 1,10000000 do
  local x = math.sin(i)
end
b = os.clock()
print(b-a) --1.113454



a = os.clock()
local sin = math.sin
for i = 1,10000000 do 
 local x = sin(i)
end
b = os.clock()
print(b-a) --0.75951

关于字符串

1. 使用运算符..

每次拼接都需要申请新的空间,旧的 result 对应的空间会在某时刻被Lua的垃圾回收期GC,且随着result不断增长,越往后会开辟更多新的空间,并进行拷贝操作,产生更多需要被GC的空间,所以性能降低。

2. 使用 table.concat (table [, sep [, start [, end]]]) 函数

table.concat 底层拼接字符串的方式也是使用运算符.. ,但是其使用算法减少了使用运算符..的次数,减少了GC,从而提高效率。主要思路:采用二分思想,用栈存储字符串,新入栈的字符串与下方的字符串比较长度,大于则使用运算符..拼接成新字符串,并移除栈顶的字符串,不断向下直至遇到长度更大的字符串或者栈底,这样保持最大的字符串位于栈底,栈呈现金字塔的形状,最终在使用运算符..将栈中的字符串拼接成最终的字符串。

在大字符串连接中,我们应避免..。应用table来模拟buffer,然后concat得到最终字符串。

(补充:这个在lua5.3有优化。如果是连续的..性能会更好,也就是说如果写在同一行中

例如: aa..bb..cc.dd.ee)

标签:c#,lua,C#,float,transform,Lua,字符串,交互,id
From: https://www.cnblogs.com/comradexiao/p/18489648

相关文章

  • Prufer序列和Cayley公式
    首先定义无根树中度数为1的节点是叶子节点。找到编号最小的叶子并删除,序列中添加与之相连的节点编号,重复执行直到只剩下2个节点。这个序列为这棵树的Prufer序列。一棵有\(n\)个节点的无根树的Prufer序列的长度为n-2。显然,一棵无根树可以一一对应一个Prufer序列。而......
  • mac电脑上安装多个版本的node
    前言开发旧项目时,使用低版本Nodejs。开发新项目时,需使用高版本Node.js。可使用n同时安装多个版本Node.js,并切换到指定版本Node.js。出处:mac电脑上安装多个版本的node1、全局安装npminstall-gn2、安装指定node版本#比如我的电脑上安装了一个16.13.2的和一个18.16......
  • HTML <canvas> 项目 画个房子
    在HTML、CSS、JS拼搏30余载,终于,有了自己的房子。......
  • 超强AI绘画工具StableDiffusion,SD整合包V4.9 来了 版本win加mac安装包以及搭载PS安装
    众所周知,StableDiffusion是非常强大的AI绘图工具,今天为大家带来的是StableDiffusionSD整合包v4.9版本安装说明。这里带来的安装版本是9月最新整合包sd-webui-aki-v4.9版本。WIN加MAC一、整合包详细说明1、整合包升级的内容:torch2、xformers0.0.17、cu......
  • Arc浏览器打包扩展程序教程
    如何分享已安装的Chrome插件并进行离线安装如果你想将自己已安装的Chrome插件分享给朋友,进行离线安装,可以按照以下步骤操作:步骤1:进入扩展程序管理页面打开Chrome浏览器,在地址栏中输入arc://extensions/进入扩展程序页面。或者点击菜单栏下按钮,选择“扩展程序......
  • Excel-Ctrl+Enter键的妙用
    一、Ctrl+Enter键的妙用 1.1 Ctrl+Enter键在多连续区域输入相同内容比如我要在一块区域内输入相同的数据,我首先选中这块区域,然后在第一个表格内输入数据-输入之后-(不要按回车键)按Ctrl+Enter键,即可全部表格输入同一数据  1.2 Ctrl+Enter键在非连续区域输入相同内容 先......
  • podman 无根用户分配系统CPU、内存等系统资源,提示cgroup相关权限不足
    问题:在使用Podman以无根用户(rootless)模式创建容器时,如果遇到分配系统CPU等资源时提示cgroup权限不足,这是因为无根用户没有直接访问cgroup相关资源的权限。以下是一些解决方法(目前采用的办法3临时解决,,主要是更改系统目录权限sudochown-R$USER:$USER/sys/fs/cgro......
  • React项目中的antd,Form和Table如何一起使用
    React项目中的antd,Form和Table如何一起使用在项目中我们可能会遇到单独的表格,单独的表单这样使用。但是稍微复杂一点,如果是表单中存在一个类似于表格的列表,我们能够动态的去增加删除。或者是表格中的每一行中的某一列或者多个列是表单信息,那么我们又应该怎么实现呢?const[toForm......
  • 17track物流查询平台 last-event-id 参数逆向分析
    声明本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作......
  • 【JS逆向百例】某赚网 WebSocket 套 Webpack 逆向分析
    声明本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作......