[英]Does Lua optimize the ".." operator?
我必須執行以下代碼:
local filename = dir .. "/" .. base
循環中數千次(這是打印目錄樹的遞歸)。
現在,我想知道 Lua 是否在一個 go 中連接 3 個字符串(dir、“/”、base)(即,通過分配一個足夠長的字符串來保存它們的總長度),或者它是否通過在兩個步驟:
local filename = (dir .. "/") -- step1
.. base -- step2
最后一種方式在內存方面效率低下,因為分配了兩個字符串而不是一個。
我不太關心 CPU 周期:我主要關心 memory 的消耗。
最后,讓我概括一下這個問題:
Lua 在執行以下代碼時是否只分配一個字符串,還是 4 個?
local result = str1 .. str2 .. str3 .. str4 .. str5
順便說一句,我知道我可以這樣做:
local filename = string.format("%s/%s", dir, base)
但我還沒有對其進行基准測試(內存和 CPU 方面)。
(順便說一句,我知道 table:concat()。這增加了創建表的開銷,所以我想它不會在所有用例中都有益。)
一個獎勵問題:
如果 Lua 沒有優化 ".." 運算符,那么定義一個 C function 用於連接字符串utils.concat(dir, "/", base, ".", extension)
、con、
雖然Lua對..
使用進行了簡單的優化,但你仍然應該小心地在緊密循環中使用它,特別是在連接非常大的字符串時,因為這會產生大量垃圾,從而影響性能。
連接多個字符串的最佳方法是使用table.concat
。
table.concat
允許您將表用作所有要連接的字符串的臨時緩沖區,並且只有在完成向緩沖區添加字符串后才執行連接,如下面的愚蠢示例所示:
local buf = {}
for i = 1, 10000 do
buf[#buf+1] = get_a_string_from_somewhere()
end
local final_string = table.concat( buf )
可以看到..
的簡單優化分析以下腳本的反匯編字節碼:
-- file "lua_06.lua"
local a = "hello"
local b = "cruel"
local c = "world"
local z = a .. " " .. b .. " " .. c
print(z)
luac -l -p lua_06.lua
的輸出如下(對於Lua 5.2.2):
main (13 instructions at 003E40A0) 0+ params, 8 slots, 1 upvalue, 4 locals, 5 constants, 0 functions 1 [3] LOADK 0 -1 ; "hello" 2 [4] LOADK 1 -2 ; "cruel" 3 [5] LOADK 2 -3 ; "world" 4 [7] MOVE 3 0 5 [7] LOADK 4 -4 ; " " 6 [7] MOVE 5 1 7 [7] LOADK 6 -4 ; " " 8 [7] MOVE 7 2 9 [7] CONCAT 3 3 7 10 [9] GETTABUP 4 0 -5 ; _ENV "print" 11 [9] MOVE 5 3 12 [9] CALL 4 2 1 13 [9] RETURN 0 1
您可以看到只生成了一個CONCAT
操作碼,盡管腳本中使用了許多..
運算符。
要完全了解何時使用table.concat
您必須知道Lua字符串是不可變的 。 這意味着每當您嘗試連接兩個字符串時,您確實創建了一個新字符串(除非結果字符串已被解釋器實例化,但這通常不太可能)。 例如,請考慮以下片段:
local s = s .. "hello"
並假設s
已經包含一個巨大的字符串(比方說,10MB)。 執行該語句會創建一個新字符串(10MB + 5個字符)並丟棄舊字符串。 所以你剛剛為垃圾收集器創建了一個10MB的死對象來應對。 如果你反復這樣做,你最終會占用垃圾收集器。 這是真正的問題..
這是典型的使用情況下,有必要收集,最后一個字符串的所有片段在表中,並使用table.concat
就可以了:這將無法避免垃圾的產生(調用table.concat
后所有的部分都將是垃圾,但是你會大大減少不必要的垃圾。
..
在這種情況下, table.concat
會給你帶來更差的性能,因為:
table.concat
(函數調用開銷比使用內置的..
運算符多次影響性能)。 table.concat
,尤其是在滿足以下一個或多個條件時:
..
優化只在同一個表達式中起作用); 請注意,這些只是經驗法則。 如果性能真的至關重要,那么您應該對代碼進行分析。
無論如何Lua在處理字符串時與其他腳本語言相比要快得多,所以通常你不需要太在意。
在您的示例中, ..
運算符是否進行優化對於性能來說幾乎不是問題,您不必擔心內存或CPU。 還有用於連接多個字符串的table.concat
。 (請參閱Lua中的編程 )以了解table.concat
。
回到你的問題,在這段代碼中
local result = str1 .. str2 .. str3 .. str4 .. str5
Lua只分配一個新字符串,從luaV_concat
Lua相關源檢查這個循環:
do { /* concat all strings */
size_t l = tsvalue(top-i)->len;
memcpy(buffer+tl, svalue(top-i), l * sizeof(char));
tl += l;
} while (--i > 0);
setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl));
total -= n-1; /* got 'n' strings to create 1 new */
L->top -= n-1; /* popped 'n' strings and pushed one */
你可以看到Lua在這個循環中連接了n
字符串,但是最后只將一個字符串推回到堆棧,這是結果字符串。
順便說一句,我知道 table:concat()。 這增加了創建表的開銷,所以我想它不會在所有用例中都有益。
在這個特定的用例(和類似的用例)中,如果您擔心創建大量垃圾表,則可以考慮重用表:
local path = {}
...
-- someplace else, in a loop or function:
path[1], path[2] = dir, base
local filename = table.concat(path, "/")
path[1], path[2] = nil
...
您甚至可以將其概括為“concat”實用程序:
local rope = {}
function string_concat(...)
rope = {...} -- prepare rope
local res = table.concat(rope)
for i = 1, select("#", ...) do rope[i] = nil end -- clear rope
return res
end
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.