I have to execute the following code:
local filename = dir .. "/" .. base
thousands of times in a loop (it's a recursion that prints a directory tree).
Now, I wonder whether Lua concatenates the 3 strings (dir, "/", base) in one go (i.e., by allocating a string long enough to hold their total lengths) or whether it does this the inefficient way by doing it internally in two steps:
local filename = (dir .. "/") -- step1
.. base -- step2
This last way would be inefficient memory-wise because two strings are allocated instead of just one.
I don't care much about CPU cycles: I care mainly about memory consumption.
Finally, let me generalize the question:
Does Lua allocate only one string, or 4, when it executes the following code?
local result = str1 .. str2 .. str3 .. str4 .. str5
BTW, I know that I could do:
local filename = string.format("%s/%s", dir, base)
But I've yet to benchmark it (memory & CPU wise).
(BTW, I know about table:concat(). This has the added overhead of creating a table so I guess it won't be beneficial in all use cases.)
A bonus question:
In case Lua doesn't optimize the ".." operator, would it be a good idea to define a C function for concatenating strings, e.g. utils.concat(dir, "/", base, ".", extension)
?
Lua hashes all strings on creation, this makes comparison and using them in tables very fast and reduces memory use since all strings are stored internally only once.
It integrates seamlessly with C and for an interpreted language it's super efficient. Rather than including every library under the sun in the standard lib, Lua is compact and the entire compiled binary is measured in kilobytes. You can read the Lua source code in C and comprehend a lot of it in a weekend.
Following table shows all the logical operators supported by Lua language. Assume variable A holds true and variable B holds false then − Called Logical AND operator. If both the operands are non zero then condition becomes true. (A and B) is false. Called Logical OR Operator.
In Lua ‘result’ variable will hold either the value of ‘operand1’ or ‘operand2’ instead of the boolean true or false values depending on the value that variable holds. How does an ‘and’ operator work in Lua? In Lua language, there are basically 3 logical operators which are ‘and’, ‘or’ and ‘not’.
Lua is famous for its performance, and it deserves its reputation among sc ripting languages. Nevertheless, we all know that performance is a key ingredie nt of program- ming. It is not by chance that problems with exponential time complexity are called intractable . A too late result is a useless result.
Have you studied general tips for keeping Lua performance high? i.e. know table creation and rather reuse a table than create a new one, use of 'local print=print' and such to avoid global accesses.
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.
..
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:table.concat
(the function call overhead impacts performance more than using the built-in ..
operator a few times).table.concat
, if you need to concatenate many strings, especially if one or more of the following conditions are met:..
optimization works only inside the same expression);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.
In your example, whether the ..
operator does optimization is hardly a problem for the performance, you don't have to worry about memory or CPU. And there's table.concat
for concatenating many strings. (See Programming in Lua) for the use of table.concat
.
Back to your question, in this piece of code
local result = str1 .. str2 .. str3 .. str4 .. str5
Lua allocates only one new string, check out this loop from Lua's relevant source in 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 */
You can see that Lua concatenate n
strings in this loop but only pushes back to the stack one string in the end, which is the result string.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With