C#跟Lua如何超高性能传递数据

UWA学堂上线那天,我买了招文勇这篇Lua交互的课程,19块还算值,但是前段时间太忙,一直没空研究,他的demo是基于xlua的,今天终于花了大半天时间在tolua下跑起来了,记录一下我的理解

C#跟Lua如何超高性能传递数据

性能,仍然是Lua中与C#混用的大坑

Lua跟C#交互的性能问题是老生常谈的了,c#跟lua数据交互是通过lua虚拟栈,进行压栈、出栈来传递的,一次调用就需要执行很多指令,性能会随着调用次数的频繁,函数参数的增多而变差。直接操作内存的方式,可以在c#端修改lua内存,省去了操作虚拟栈,函数调用的大把指令,性能也就很高效了

腾讯的UnLua(给虚幻4用的)中也有类似的直接操作内存的交互方式,看来这种方式会渐渐成为主流,毕竟性能摆在这呢

UnLua

Lua跟C#高效共享大量数据的一种方法

原理其实很简单,在c#端定义好lua table的结构体,必须在内存中对齐lua端的table,然后在c#端拿到lua table的指针,读写这块内存,就能读写这个lua table了。

是不是觉得非常简单,哈哈哈哈。感觉自己马上就能弄出来了

说起来简单,做起来难,对技术还是多有一些敬畏之心(大神,就当我没说过吧)

想要实现这套东西,还得搞懂很多个问题,下面开始一一讲解 Lua Table结构体是什么样的?

想在c#端写一个lua table结构体,那就先看看lua端这个结构体是怎么实现的吧。在tolua下,我们使用的是luajit,jit的源码跟lua是不一样的,luajit又分32位跟64位。所以我们这个table结构体也需要做多套才行

luajit中的GCTab就是Table的结构体了

typedef struct GCtab { GCHeader; uint8_t nomm; /* Negative cache for fast metamethods. */ int8_t colo; /* Array colocation. */ MRef array; /* Array part. */ GCRef gclist; GCRef metatable; /* Must be at same offset in GCudata. */ MRef node; /* Hash part. */ uint32_t asize; /* Size of array part (keys [0, asize-1]). */ uint32_t hmask; /* Hash part mask (size of hash part - 1). */ #if LJ_GC64 MRef freetop; /* Top of free elements. */ #endif } GCtab;

GCHeader是每一个GC对象都要包含的一个宏,定义了这些属性

#define GCHeader GCRef nextgc; uint8_t marked; uint8_t gct

lua的table支持数组、哈希表两种用法,甚至可以同时是数组又是哈希表。我们主要处理数组的数据交互,结构体中的MRef array;就是这个table的所有数据存储的地方了,而asize就等于这个数组的长度+1。所以我们重点关注这2个字段的内存地址

如何设计c#端的table结构体呢?

我们把GCTab结构体展开成这样看

GCRef nextgc; uint8_t marked; uint8_t gct; uint8_t nomm; int8_t colo; MRef array; GCRef gclist; GCRef metatable; MRef node; uint32_t asize; uint32_t hmask; MRef freetop;//这个是64位的才会有

GCRef 跟 MRef 都是一个jit中封装的指针类型,会自动根据宏展开为32位跟64位。
GCRef 表示这是一个GC对象的指针
MRef 表示非GC对象的内存指针

在c#中都可以用IntPtr类型代替

uint8_t 是8字节的,我们把4个8字节的放在一起,可以用一个int32位占用

那么转换到c#中,结构体就变成了这样

// GC64 version public struct LuaJitGCtabGC64 { IntPtr nextgc; UInt32 masks; IntPtr array; IntPtr gclist; IntPtr metatable; IntPtr node; UInt32 asize; UInt32 hmask; IntPtr freetop; // only valid for LJ_GC64 } 指针array指向的数据是什么?

在lj_tab.c中看tab的实现,我们很快就能找到array里存的是TValue结构,TValue其实是一个联合体。

联合体是多个结构体可以共享同一块内存,访问的时候可以用不同的结构体方式去访问。具体什么是联合体可以自行百度哦

TValue源码

/* Tagged value. */ typedef LJ_ALIGN(8) union TValue { uint64_t u64; /* 64 bit pattern overlaps number. */ lua_Number n; /* Number object overlaps split tag/value object. */ #if LJ_GC64 GCRef gcr; /* GCobj reference with tag. */ int64_t it64; struct { LJ_ENDIAN_LOHI( int32_t i; /* Integer value. */ , uint32_t it; /* Internal object tag. Must overlap MSW of number. */ ) }; #else struct { LJ_ENDIAN_LOHI( union { GCRef gcr; /* GCobj reference (if any). */ int32_t i; /* Integer value. */ }; , uint32_t it; /* Internal object tag. Must overlap MSW of number. */ ) }; #endif #if LJ_FR2 int64_t ftsz; /* Frame type and size of previous frame, or PC. */ #else struct { LJ_ENDIAN_LOHI( GCRef func; /* Function for next frame (or dummy L). */ , FrameLink tp; /* Link to previous frame. */ ) } fr; #endif struct { LJ_ENDIAN_LOHI( uint32_t lo; /* Lower 32 bits of number. */ , uint32_t hi; /* Upper 32 bits of number. */ ) } u32; } TValue;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyyzwy.html