Although Lua performs a simple optimization on ..
usage, you should still be careful to use it in a tight loop, especially when joining very large strings, because this will create lots of garbage and thus impact performance.
The best way to concatenate many strings is with table.concat
.
table.concat
lets you use a table as a temporary buffer for all the strings to be concatenated and perform the concatenation only when you are done adding strings to the buffer, like in the following silly example:
local buf = {}
for i = 1, 10000 do
buf[#buf+1] = get_a_string_from_somewhere()
end
local final_string = table.concat( buf )
The simple optimization for ..
can be seen analyzing the disassembled bytecode of the following script:
-- file "lua_06.lua"
local a = "hello"
local b = "cruel"
local c = "world"
local z = a .. " " .. b .. " " .. c
print(z)
the output of luac -l -p lua_06.lua
is the following (for Lua 5.2.2 - edit: the same bytecode is output also in Lua 5.3.6):
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
You can see that only a single CONCAT
opcode is generated, although many ..
operators are used in the script.
To fully understand when to use table.concat
you must know that Lua strings are immutable. This means that whenever you try to concatenate two strings you are indeed creating a new string (unless the resulting string is already interned by the interpreter, but this is usually unlikely). For example, consider the following fragment:
local s = s .. "hello"
and assume that s
already contains a huge string (say, 10MB). Executing that statement creates a new string (10MB + 5 characters) and discards the old one. So you have just created a 10MB dead object for the garbage collector to cope with. If you do this repeatedly you end up hogging the garbage collector. This is the real problem with ..
and this is the typical use case where it is necessary to collect all the pieces of the final string in a table and to use table.concat
on it: this won't avoid the generation of garbage (all the pieces will be garbage after the call to table.concat
), but you will greatly reduce unnecessary garbage.
Conclusions
- Use
..
whenever you concatenate few, possibly short, strings, or you are not in a tight loop. In this case table.concat
could give you worse performance because:
- you must create a table (which usually you would throw away);
- you have to call the function
table.concat
(the function call overhead impacts performance more than using the built-in ..
operator a few times).
- Use
table.concat
, if you need to concatenate many strings, especially if one or more of the following conditions are met:
- you must do it in subsequent steps (the
..
optimization works only inside the same expression);
- you are in a tight loop;
- the strings are large (say, several kBs or more).
Note that these are just rules of thumb. Where performance is really paramount you should profile your code.
Anyway Lua is quite fast compared with other scripting languages when dealing with strings, so usually you don't need to care so much.