C 跟Lua如何超高效能傳遞資料

2022-06-01 16:39:34 字數 3153 閱讀 1507

uwa學堂上線那天,我買了招文勇這篇lua互動的課程,但是前段時間太忙,一直沒空研究,他的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互動的課程,19塊還算值,但是前段時間太忙,一直沒空研究,他的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客戶端,使用很方便。前幾天微博發生了一起大的系統故障,很多技術的朋...