其他分享
首页 > 其他分享> > lua和cs交互优化

lua和cs交互优化

作者:互联网

整个思路的核心就是:

 

1、通过Lua_topointer,直接获取Lua table的内存指针。
2、由于Lua/LuaJIT的table内存结构是可以确认的,我们可以对照其C代码在C#中声明结构体,这样就可以通过table指针拿到array的指针以及array的长度。
3、但是,这里有一个难点,就是要处理Lua/LuaJIT的差异,以及在不同编译选项下产生出来的32位、64位的差异。所以可以看到我们是分LuaAdapter.cs和LuaJitAdapter.cs两套实现,并且各自提供了32/64位的结构体声明。
4、不管是Lua还是LuaJIT,array数组存储的不是int或者double,而是一个叫TValue的联合体,TValue除了存储数值本身,还存储了类型信息。我们在读写的时候,需要先判断类型信息,不然就会无法获得正确的结果。
5、在了解这些信息之后,整个过程就是:拿到table指针,用对应平台的结构体指针获得array指针,再通过数组index拿到array中正确位置的TValue,最后根据TValue的类型信息获得/写入int或者double。

文章最后提供了实现的下载链接,此处演示如何使用这个库。

Lua端:

C#端:

 

 

具体使用流程:

如何应用

有了这个跨语言共享数据的方案,我们就可以大胆将主要的数据放在Lua层,而不用再为跨语言性能而过度牺牲热更代码的覆盖率。具体怎么设计代码的架构来利用这个数据共享方式呢?

以《赤潮》这样的产品为例:


在以往的方案里,由于性能和热更新两者高度矛盾,要同时做到以上的点,十分困难。 而现在,借助跨语言数据共享,我们可以这样实现:


这一切之所以可行,得益于我们可以低消耗跨语言传递数据,否则,我们放在Lua的功能(网络同步/数据表/玩法规则/声明周期管理/技能AI模板节点),会因为需要频繁跟C#交互,产生巨大的性能瓶颈。 可见,高效地在两个语言之间共享数据,是非常重要的。


此外,如果你的项目本身是用Lua做的,要迁移到C#,你也可以利用这个共享机制很轻松地将局部代码转移过去。

 

性能对比

由于在Lua和C#间传递数据的方法很多,我们对比一下通过传统C#版Lua导出传参,以及在C导出API给Lua直接读写C内存的性能,进行对比。

环境:Windows + i7 + Unity 2018.3.11f1,Lua使用5.3.5,LuaJIT使用2.1.0beta3(启用interpreter模式)

1、可以看到,新方案(操作1/2和操作3/4)比起传统直接Lua调用C#传递数据(操作6),性能优化非常大。新方案在两个语言都做到近乎原生读写的性能,读写成本基本不再是瓶颈。因此,将代码按需求分布在Lua和C#之间,将成为可能。

2、其中,C#端的操作3/4性能会较慢,主要原因在于两方面: 

3、而Lua原生读写数组(操作1)的效率也比通过C API访问共享内存(操作5)要高一些,提升效率大概是2~3倍的水平。而C#端我们没有对比,因为我们的实现本质就是访问共享内存的方式,所以性能本质上是一样的,完全看共享内存存储方式的具体实现(比如是否像Lua一样需要判断数据类型)。另外考虑到我们访问不需要编译C代码,所以这个方案也是非常有优势的。

4、另外,由于数据是共享的,就没有必要在Lua和C#之间分配两套内存空间存数据,也就提供了节省内存的可能性。

5、事实上,了解Lua底层的朋友会知道,Lua数组(采用1~n连续整数键值的表)比Lua hashtable(即有带命名字段的表)访问起来要快,且节省内存(TValue的内存占用大概是Node+Key/Value的四分之一)。所以即使使用面向对象的方式开发,从性能最优的角度,我们也鼓励用Lua数组的方式来替代Lua hashtable的方式,如下代码所示: 

 

附录:使用细节说明及源码下载

1. 如何整合插件

a) 由于代码使用了unsafe code,所以需要在Unity的player settings勾选Allow unsafe code。
b) 下载本文附件中的代码,将LuaAdapter.cs和LuaJitAdapter.cs拷贝到工程的任意目录。
c) 代码默认按照xLua的标准开发,但如果你使用的不是xLua也很容易集成。

d) 在Lua初始化后,调用LuaTableCSharpAccess.RegisterPinFunc(L);注册函数,其中参数L是lua state的IntPtr。
e) LuaCSharpArr.lua.txt中包含了LuaCSharpArr的整个定义声明,可以直接require使用,如果你的Lua代码require机制不同,将代码复制到你的Lua可以访问到的地方即可。
f) 参考示例代码LuaTestScript.lua.txt以及LuaTestBehaviour.cs使用Lua端的LuaCSharpArr和C#端的LuaArrAccess。
g) 如果想运行测试用例,在空场景中建立一个GameObject,将LuaTestBehaviour拖进去,并将里头的LuaScript字段附上LuaTestScript.lua.txt即可。

2. 理解Lua array与hashtable

a) 你需要知道,Lua table实际内部是包含一个数组(array)和hashtable来共同存储所有数据的。其中array用于存储键值为1~n的数据,且1~n必须连续。而其他的数据,会被存放在hashtable内。
b) 必须要注意,Lua数组要求key必须是连续的整数(1~n),如果中间有空洞,那么可能出现的情况是后面的数据会被放到hashtable存储,也就无法在LuaArrAccess读取,所以我们提供了预分配机制,防止自己插入的时候出现失误。

3. 安全读写与访问

a) 要大规模在工程中使用,那么代码的安全性就很重要,将lua底层数据结构暴露这个事情,本身会破坏Lua的安全性,错误的操作可能会导致严重的内存错误。因此我们提供的实现做了一些机制避免出现问题。

b) 使用注意

4. 进一步的性能提升

a) LuaArrAccess的代码为了易用性和安全性,牺牲了相当程度的性能,读者如果对性能有更高要求并且有意愿修改源码的话,可以尝试以下方法提高性能。

5. 其他问题

a) 目前提供的实现,只支持数组存储int/double,不支持其他类型(bool/string等)。由于Lua/LuaJIT在默认编译方式下使用double存储浮点数,所以不提供float相关的接口。
b) 代码只支持读写数组,无法读写hashtable。这里也额外提一点,Lua hashtable虽然使用便利,但是在读写效率以及内存占用上,都比lua array要差不少。所以在我们的项目实践中,会有大量的Lua class使用array来存储字段。
c) 事实上这个方法可以举一反三,推广到用于直接在C#绑定访问lua中的某个表的一个字段,或者访问完整的key-value table,不过由于table访问的复杂性,实现起来会相对复杂一些,不在本文讨论的范围内,读者可以进一步探索。
d) 本文提供的代码并非文中提到的《赤潮》所使用的版本,因为《赤潮》使用的代码基于ulua+luajit2.1.0beta2,也未实现文中所提到的安全访问特性,且需要修改LuaJIT源码,所以代码会有大量不同。
e) 本文的源码已通过以下平台测试(xLua 2.1.14 + unity2018.3.5)。

f) 另由于Lua/LuaJIT、xLua/sLua/uLua多版本差异以及Mono/il2cpp/跨平台带来的复杂性,本文代码无法确保完全覆盖所有版本和所有平台的组合情况,如果使用中遇到问题,欢迎在下方留言反馈。

标签:LuaArrAccess,Lua,C#,代码,lua,数组,cs,交互,LuaJIT
来源: https://www.cnblogs.com/chenggg/p/11186032.html