在Lua開發中我們會用到"..."這樣的文法以表示不確定參數的輸入及返回. 但一些用法可能導致效能上的問題你大約從來沒有注意過, 它卻在不知不覺中對程式的效能產生著潛移默化的影響.
首先來看例子會很直接, 這個例子展示了在不同的"..."的使用方式下, 執行10000次是對記憶體產生的影響. Lua 5.1環境.
collectgarbage("stop")
print("init memory:"..collectgarbage("count"))
function test(...)
return 1 + select(1, ...) --A result memory:22.2587890625
--select(1,...);return 1 --B result memory:22.2587890625
--return 1, ... --C result memory:22.0947265625
--return 1 + arg[1] --D result memory:803.3623046875
--return 1 --E result memory:803.3544921875
--return 1 + 1 --F result memory:803.3544921875
--return 1, unpack(arg) --G result memory:803.5185546875
--return 1, arg[1], ... --H error attempt to index local 'arg' (a nil value)
--return 1 + #{...} --I result memory:490.9072265625
--select(1,...);return 1,{1} --J result memory:491.0712890625
end
print("start memory:"..collectgarbage("count"))
for i = 1, 10000 do
test(i)
end
print("result memory:"..collectgarbage("count"))
第一步要把GC關掉, 以免中途引發記憶體回收影響我們對記憶體大小的觀察. test方法中的每一行都是一種情況, 運行時請將其他行注釋掉, 保持一行在執行, 每行後面的注釋result momery則是在當前用法下記憶體大小的結果.
從上面例子的結果中, 我們看到了在對"..."應用不同的使用方式時記憶體變化的情況, 同樣的東西, 記憶體佔用卻是從20多到800多的巨大差異, 在偶爾幾次的調用中這個差異是微不足道的, 一旦被大量重複調用, 缺陷立刻被放大化, 有的方式會頻繁的將"..."的內容建立為一個暫存資料表, 在函數結束後stack上的實值型別參數內容將被清空, 建立的暫存資料表卻留在了heap中, 大量暫存資料表的出現將可能引起頻繁的記憶體回收導致影響效能, 這就是應該注意的地方. 同時也看到了一些有趣的現象.
- 調用語句E和F, 這裡沒有使用"..."中的參數, 它卻建立了暫存資料表.
- 調用語句B, 隨便應用下"...", 哪怕不真正的用它, 也不會建立暫存資料表.
- 調用語句H, 一旦應用了"...", arg的暫存資料表變數就不會被建立. 至此看出些端倪了吧, 應證了上兩個現象的結果.
- 調用語句I, 可以看出arg暫存資料表的記憶體消耗竟然快是自建表的近2倍.
知道這些規律了, 對寫程式應該是很有協助的, 可能你還在發愁為何gc會被頻繁調用, 問題就在這裡了.
更多討論參見CWDG論壇: http://bbs.cwowaddon.com/thread-6700-1-1.html