Lua的local變量探究

  歡迎參與討論,轉載請註明出處。
   本文轉載自:https://musoucrow.github.io/2018/04/17/lua_local/

前言

  先前看到用好Lua+Unity,讓性能飛起來—LuaJIT性能坑詳解一文中提到:

3.2 寄存器分配失敗->減少local變量、避免過深的調用層次
很不幸的一點是,arm中可用的寄存器比x86少。LuaJIT爲了速度,會儘可能用寄存器存儲local變量,但是如果local變量太多,寄存器不夠用,目前JIT的做法是:放棄治療(有興趣可以看看源碼中asm_head_side函數的註釋)。因此,我們能做的,只有按照官方優化指引說的,避免過多的local變量,或者通過do end來限制local變量的生命週期。

  對此自然是可以理解的,哪怕是一般語言,local變量過多也會有堆棧溢出的問題。不過我對此一直有個隱憂:Lua是擁有模塊級local變量的,不知是否也受此規則影響?儘管有此隱憂,卻一直沒有去做相關的探究。恰逢今日遇到相關話題,便來個刨根問底吧。

200限制

  首先的發現是:一段過程下最多擁有200個local變量,且do end不算。類似這樣:

local Class = {}

local test1 = 1
local test2 = 2
... --to 199

return Class

  如果超過199,則會報出main function has more than 200 local variables的錯誤。當然這裏說的是一段過程,所以函數是另算的,同樣一個函數的過程最多也不能超過200個local變量(調用函數則算轉入下一個過程了)。
  這個限制是Lua與LuaJIT共有的,顯然是想限制local數量的泛濫。

函數嵌套調用

  接下來便是試試函數嵌套調用了:

function Class.Do(v)
    if (v > n) then --n is a custom value
        return
    end

    local test1 = 1
    ... -- to 199

    Class.Do(v + 1)
end

  注意參數v也算是local變量的一員,所以test變量最多隻能延伸到199個。以此進行遞歸調用的話,根據實驗結果來看:

版本 嵌套上限 local變量上限
LuaJIT 325 65000
Lua5.3 4975 995000

  測試的環境爲macOS x86-64,LuaJIT方面無論JIT開啓與否結果皆一致。根據前文所言來看,到了ARM環境這個數量將會進一步下降。雖然從對比來看差距有點大,但實際上在函數調用方面也算夠用了。

模塊級local變量

  接下來便是我最關心的一點了:以上local變量上限是否會影響到模塊級local變量?所謂模塊級local變量即作用域爲整個文件:

--test.lua
local Class = {}
local function Func()
end
...

return Class

  這種模塊級local變量在Lua開發的應用還是很廣泛的,它能有效的做到信息分隔的效果。但若是這些變量也受之前的上限規則影響,咁就撲街了!

  首先是測試加載多個滿載local變量的模塊:

for n=1, 5000 do
    require("test" .. n)
end

  天可憐見,無論讀取多少個文件,都不會存在上限問題。可見對於模塊級local變量的處理是不一樣的。到了這裏基本上可以放心了,不過爲防萬一,我還做了模塊的嵌套引用實驗:

---test1.lua

local Class = {}
print("1")
local Next = require("test2")
local test1 = 1
local test2 = 2
... --to max
return Class

  以這種形式生成了5000個文件,以此進行嵌套引用,結果也是成功通過了。由此可見,對於模塊級local變量是可以放心地去使用了。

後記

  儘管模塊級local變量是可以隨便用了,但是也要考慮到熱更新方面的問題:若是選擇使用模塊級local變量去存儲模塊的數據,那麼在熱更新方面的處理將會變得十分麻煩。從這點考慮的話,模塊級local變量最好只是用於引用別的模塊爲妙。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章