繞過 c api 直接訪問 lua 表
今天試了一下一個想法:繞過 lua 提供的 C API 直接去訪問 lua 的表結構,提供在性能及其重要的環境高效訪問數據結構的方法。
例如:我們需要在 lua 和 C 中共享一個 vector 3 結構,有兩種實現方法:一、把 C struct 實現為 lua 中的 userdata ,然后給 userdata 加上 metatable 以供 lua 中訪問內部數據;二、在 lua 中使用一個 table 實現這個 vector3 結構,類似 { x = 0.0 , y = 0.0, z = 0.0 } 這樣;然后在 C 里通過 c api ( lua_rawget/lua_gettable/lua_getfield ) 來訪問里面的數據。
前一種方法會導致在 Lua 中訪問成本加大、而后一種方法增加的是 C 中訪問數據的成本。如果我們只在少數性能敏感的地方通過 C 去操作數據結構,那么第二種方法看起來更簡單靈活一些。這樣,不需要 C 介入的地方,是沒有額外開銷的。畢竟、通過 metamethod 索引 userdata 的成本比直接索引一個普通的 table 要重的多。
但是、第二種方法會導致 C 訪問數據的成本較大。我們采用 C 代碼去處理 vector 數據結構,一定是考慮到性能熱點,在語言邊界上損失性能感覺不太劃算。我覺得或許可以采取一個技巧來加快它。
對于標準的 Lua 實現,構造好的 hash 表,在不添加新 key 的前提下,讀寫已有的 key ,value 所在的 slot 是不變的。如果我們能記住 slot 的位置,那么就可以繞過 hash 過程、也不需要把 key (這里是一個 string)壓棧,直接讀寫值了。
而且,對于同一個 lua_State 從一個空表開始,按一致的次序寫入相同的 key ,內部數據結構也一定相同。我們可以利用這一點,為同類結構一次性生成索引表。
我寫了一小段代碼驗證我的想法,感覺是可行的: https://gist.github.com/cloudwu/09fca725cb9177d809790b6a7ecdac20 。
你可以先創建一個 4 個 slot 的 hash 表,key 分別是 x y z __vector 。這第 4 個 key __vector 是一個標記,表示這是個規整過的數據結構,x y z 都是浮點數,且一定在固定的 slot 里。
void vector_init(lua_State *L, struct vector_offset *vo) 可以用來生成 slot 號的結構 struct vector_offset 。每個 lua_State 只用生成一次,然后就可以永久保存在 C 的數據結構中。
然后我們用 vector_get 可以獲得內部數據結構 Table * ,這個結構定義在 lobject.h 中,是一個內部 h 文件,這里可以借用一下。之后,就可以用宏 X Y Z 去訪問這個 Table * 了。
vector_get 中,會檢查指定的 table 是否是規整化的 vector 結構,如果不是,就把 x y z 三項讀出來,清空 table ,再寫回去,并填上 __vector 標記。此處檢查 __vector 標記是個很輕量的操作。
這個方案適用于 Lua 5.3 ,我沒有在老版本的 Lua 上試過,但想必也是可以用的。它的好處是不需要修改任何 Lua 的實現代碼、只需要引入 Lua 本身的內部 h 文件即可。所以利用這組 api 實現的 lua 庫是可以和其它庫兼容共存的。
來自:http://blog.codingnow.com/2017/02/lua_direct_access_table.html