在uwa學堂上線那天,我買了招文勇這篇lua互動的課程,19塊還算值,但是前段時間太忙,一直沒空研究,他的demo是基於xlua的,今天終於花了大半天時間在tolua下跑起來了,記錄一下我的理解
lua跟c#互動的效能問題是老生常談的了,c#跟lua資料互動是通過lua虛擬棧,進行壓棧、出棧來傳遞的,一次呼叫就需要執行很多指令,效能會隨著呼叫次數的頻繁,函式引數的增多而變差。直接操作記憶體的方式,可以在c#端修改lua記憶體,省去了操作虛擬棧,函式呼叫的大把指令,效能也就很高效了
原理其實很簡單,在c#端定義好lua table的結構體,必須在記憶體中對齊lua端的table,然後在c#端拿到lua table的指標,讀寫這塊記憶體,就能讀寫這個lua table了。
是不是覺得非常簡單,哈哈哈哈。感覺自己馬上就能弄出來了
想在c#端寫乙個lua table結構體,那就先看看lua端這個結構體是怎麼實現的吧。在tolua下,我們使用的是luajit,jit的原始碼跟lua是不一樣的,luajit又分32位跟64位。所以我們這個table結構體也需要做多套才行
luajit中的gctab就是table的結構體了
typedef struct gctab gctab;
gcheader是每乙個gc物件都要包含的乙個巨集,定義了這些屬性
#define gcheader gcref nextgc; uint8_t marked; uint8_t gct
lua的table支援陣列、雜湊表兩種用法,甚至可以同時是陣列又是雜湊表。我們主要處理陣列的資料互動,結構體中的mref array;就是這個table的所有資料儲存的地方了,而asize就等於這個陣列的長度+1。所以我們重點關注這2個字段的記憶體位址我們把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
在lj_tab.c中看tab的實現,我們很快就能找到array裡存的是tvalue結構,tvalue其實是乙個聯合體。
tvalue原始碼
/* tagged value. */
typedef lj_align(8) union tvalue ;
#else
struct ;
, 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 fr;
#endif
struct u32;
} tvalue;
這中間有很多巨集,看著很亂,但其實我們只需要用2種模式就行了,因為我們只實現int跟double。作者給出的方式是如下這種
[structlayout(layoutkind.explicit, size = 8)]
public struct luajittvalue
但這裡我有一些我還沒弄明白,因為我實際執行起來後,不管lua賦值的是整形,還是浮點,int i始終沒有值,值都存在了double n中。那為啥作者要弄乙個int i跟uint32 it; 這個it還偏移了4位元組在c#端我們可以使用[structlayout(layoutkind.explicit)]和[fieldoffset(0)]來實現c語言中的聯合體,具體方式可以看這篇文章
結構體都定義好了,接下來我們看看怎麼讀寫乙個double
luajitgctab32* tablerawptr; //需要拿到lua端table的指標
//賦值操作
tablerawptr->array[index].n = val;
//取值操作
tablerawptr->array[index].n;
沒錯,就是這麼簡單。直接就可以操作lua記憶體了。
在lua端傳入乙個table引數過來,我們可以在c#端操作虛擬棧轉成指標
system.intptr arg0 = luadll.lua_topointer(l, 1);
看到這裡,相信大部分的謎團都已經解開了,真的自己可以實現一套出來了。
作者提供的方案裡,只支援int、double。只支援array型別的table。還有luajit64位貌似沒支援好。所以如果真正要使用的話,還要改很多東西
C 跟Lua如何超高效能傳遞資料
在uwa學堂上線那天,我買了招文勇這篇lua互動的課程,但是前段時間太忙,一直沒空研究,他的demo是基於xlua的,今天終於花了大半天時間在tolua下跑起來了,記錄一下我的理解 lua跟c 互動的效能問題是老生常談的了,c 跟lua資料互動是通過lua虛擬棧,進行壓棧 出棧來傳遞的,一次呼叫就需...
Redis 超高效能的key value資料庫
說明 redis 是乙個高效能的key value資料庫。redis的出現,很大程度補償了memcached這類keyvalue儲存的不足,在部 分場合可以對關聯式資料庫起到很好的補充作用。它提供了python,ruby,erlang,php客戶端,使用很方便。問題是這個專案還很新,可能還不足夠穩定...
超高效能 key value 資料庫 Redis
redis是乙個高效能的key value資料庫。redis的出現,很大程度補償了memcached這類keyvalue儲存的不足,在部 分場合可以對關聯式資料庫起到很好的補充作用。它提供了python,ruby,erlang,php客戶端,使用很方便。前幾天微博發生了一起大的系統故障,很多技術的朋...