歡迎參與討論,轉載請註明出處。
本文轉載自: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變量最好只是用於引用別的模塊爲妙。