從 Lua 5.2 遷移到 5.3
在 2015 年的新年里, Lua 5.3 發布了 rc3 版 。
如果回顧 Lua 5.2 的發布歷史,Lua 5.2 的 final 版是在 rc8 之后的 2011 年 12 月 17 日發布的,距離 rc1 的發布日 2011 年 11 月 24 日過去不到 1 個月。我們有理由相信正式版不遠了。( 5.3 的 rc1 是 2014 年 12 月 17 日發布的)
這次升級對 Lua 語言層面的影響非常的小,但新增加的 int64 支持,以及 string pack 、utf8 庫對開發幫助很大。所以我強烈建議正在使用 Lua 5.2 的項目盡快升級到 5.3 。相對而言,當初 5.1 向 5.2 升級的時候就痛苦的多(去掉了 setfenv ,增加了_ENV)。
我計劃在 Lua 5.3 正式發布后,將 skynet 內置的 Lua 版本升級到 5.3 ,然后著手進行 skynet 1.0 的發布工作。
在 skynet 的應用環境下,我還是需要對 lua vm 的實現打一個 patch 讓 不同的 lua vm 間可以共享 Proto 。但這個工作可以先不忙做,等正式發布后再來也可以。
目前可以先逐步升級 skynet 下的 lua 庫。
我已經在 github 項目下創建了一個叫 lua53 的分支,做了一些工作。希望有同學可以幫忙一起 review 這部分代碼。有興趣的同學可以對照 最新的 commits 來檢查這些升級做的變更。
必須做的修改是去掉 unsigned 有關的 api 調用。
lua 5.3 去掉了lua_pushunsignedlua_tounsigned等 api ,現在一律使用lua_pushinteger等。這些 api 默認操作lua_Integer這個數據類型。按文檔的說法,在你的代碼中,應該盡可能的使用lua_Integer。它默認等價于 long long ,至少保證 64 位字長(lua 5.3 可以配置成使用 32bit 整數,但在 skynet 的應用環境不會這么做)。如果需要無符號整數,可以再在 C 代碼中做強制類型轉換。
這部分工作做完后,整個代碼就可以正確編譯了。
但是,和序列化有關的庫還需要為 lua 5.3 優化。因為 lua 5.3 原生支持了整型,不需要全部轉換成 double 類型儲存數字。
之前在做數據序列化工作時(seri 庫 和 bson 庫等),為了區分一個 number 類型到底是浮點數還是整數,我采取的方法是用lua_tonumber和lua_tointeger分別取一次,然后比較兩個數值是否相等。在 lua 5.3 中,直接提供了更高效的lua_isinteger來做判斷。
由于現在直接支持 64bit 整數,就不再需要使用 lightuserdata 來保存長整數了。所以我去掉了int64 庫 。
相應的,相關的庫應該做一些調整。pbc 庫目前沒有打包在 skynet 項目中,但我已經修改完畢,晚一點再放出來。skynet 內自帶的序列化庫,以及 bson ,redis 都需要做一些調整。
btw, 再修改序列化庫時發現一個 bug ,再不支持非對齊地址訪問的架構下會有點問題,這次一并修改了。
lua 5.3 不再提倡使用 bit32 庫,而且這個庫只對 32bit 整數有效,位操作現在提供了原生的操作符支持。(注:xor 是用 ~ 而不是 ^ ,因為 ^ 已經被用于 pow 操作了)我檢索了整個代碼,發現用到 bit32 最多的是那個從 openresty 移植來的 mysql driver 。
但實際上,在 lua 5.3 中不必再使用位操作去解析數據流了。因為有新的 string.pack 這個強大的 api 。比如:
local function _get_byte8(data, i) local a, b, c, d, e, f, g, h = strbyte(data, i, i + 7) -- XXX workaround for the lack of 64-bit support in bitop: local lo = bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24)) local hi = bor(e, lshift(f, 8), lshift(g, 16), lshift(h, 24)) return lo + hi * 4294967296, i + 8 end
這個函數可以被簡化成:
local function _get_byte8(data, i) return strunpack("<I8",data,i) end
在修改過程中,我發現 openresty 里這塊代碼寫的很不 lua ,比如這個 dump 函數,
local function _dump(data) local len = #data local bytes = new_tab(len, 0) for i = 1, len do bytes[i] = format("%x", strbyte(data, i)) end return concat(bytes, " ") end
按 lua 的慣用法應該寫成:
local function _dump(data) return string.gsub(data, ".", function(x) return format("%02x ", strbyte(x)) end) end
這樣既簡潔,性能也好很多。
其實這是個普遍的問題。由于 Lua 天生是門嵌入語言,幾乎所有的 Lua 程序員都用過別的語言。所以許多 Lua 程序員帶著其他語言的經驗來寫。前段時間我就發現過另一個例子。
由于 mysql 這塊改動最多,所以特別需要有人來一起 review 和測試。當然這塊代碼還有很多可以改進的地方,暫時就沒有精力做了。如果有同學有興趣,還可以把那塊尚未完成的編碼設置加進去。
原文 http://blog.codingnow.com/2015/01/lua_52_53.html