Nic*_* M. 18 c string performance lua
我必须执行以下代码:
local filename = dir .. "/" .. base
Run Code Online (Sandbox Code Playgroud)
循环中数千次(这是一个打印目录树的递归).
现在,我想知道Lua是否一次性连接3个字符串(dir,"/",base)(即通过分配足够长的字符串来保存它们的总长度),或者它是否通过在内部执行它来实现这种效率低下的方式两个步骤:
local filename = (dir .. "/") -- step1
.. base -- step2
Run Code Online (Sandbox Code Playgroud)
最后一种方式是内存方式效率低下,因为分配了两个字符串而不是一个字符串.
我不太关心CPU周期:我主要关心内存消耗.
最后,让我概括一下这个问题:
Lua在执行以下代码时是否只分配一个字符串或4?
local result = str1 .. str2 .. str3 .. str4 .. str5
Run Code Online (Sandbox Code Playgroud)
顺便说一句,我知道我能做到:
local filename = string.format("%s/%s", dir, base)
Run Code Online (Sandbox Code Playgroud)
但我还没有对它进行基准测试(内存和CPU方面).
(顺便说一句,我知道table:concat().这会增加创建表的开销,所以我猜它在所有用例中都不会有用.)
奖金问题:
如果Lua没有优化".."运算符,那么定义用于连接字符串的C函数是否是一个好主意,例如utils.concat(dir, "/", base, ".", extension)?
Lor*_*ica 35
尽管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 )
Run Code Online (Sandbox Code Playgroud)
..可以看到简单的优化分析以下脚本的反汇编字节码:
-- file "lua_06.lua"
local a = "hello"
local b = "cruel"
local c = "world"
local z = a .. " " .. b .. " " .. c
print(z)
Run Code Online (Sandbox Code Playgroud)
输出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"
Run Code Online (Sandbox Code Playgroud)
并假设s已经包含一个巨大的字符串(比方说,10MB).执行该语句会创建一个新字符串(10MB + 5个字符)并丢弃旧字符串.所以你刚刚为垃圾收集器创建了一个10MB的死对象来应对.如果你反复这样做,你最终会占用垃圾收集器.这是真正的问题,..这是典型的用例,需要收集表中的最终字符串的所有部分并table.concat在其上使用:这不会避免生成垃圾(所有部分将是调用后的垃圾table.concat),但你会大大减少不必要的垃圾.
..每当您连接数,可能短,字符串,或者你是不是在一个紧凑的循环.在这种情况下,table.concat可能会给你带来更糟糕的表现
table.concat(函数调用开销会比使用内置..运算符多次影响性能).table.concat如果需要连接多个字符串,请使用,尤其是在满足以下一个或多个条件时:
..优化仅在同一表达式中起作用);请注意,这些只是经验法则.如果性能真的至关重要,那么您应该对代码进行分析.
无论如何Lua在处理字符串时与其他脚本语言相比要快得多,所以通常你不需要太在意.
Yu *_*Hao 10
在您的示例中,..操作员是否进行优化对于性能来说几乎不是问题,您不必担心内存或CPU.还有table.concat连接许多字符串.(参见Lua编程)供使用table.concat.
回到你的问题,在这段代码中
local result = str1 .. str2 .. str3 .. str4 .. str5
Run Code Online (Sandbox Code Playgroud)
Lua只分配一个新字符串,从Lua的相关源中检查这个循环luaV_concat:
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 */
Run Code Online (Sandbox Code Playgroud)
你可以看到Lua n在这个循环中连接字符串,但只是最后将一个字符串推回到堆栈,这是结果字符串.