Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Lua optimize the ".." operator?

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)?

like image 850
Niccolo M. Avatar asked Oct 02 '13 14:10

Niccolo M.


People also ask

Is Lua memory efficient?

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.

How efficient is Lua?

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.

What are the logical operators in Lua language?

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.

What is the result variable in Lua?

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’.

Is Lua a good language for programming?

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.

How to keep Lua performance high?

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.


2 Answers

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.

like image 56
Lorenzo Donati -- Codidact.com Avatar answered Oct 17 '22 23:10

Lorenzo Donati -- Codidact.com


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.

like image 32
Yu Hao Avatar answered Oct 17 '22 23:10

Yu Hao