Optimisation Coding Tips

lua-users home
wiki

Lua 5.1 Notes

--DavidManura

Note that Roberto Ierusalimschy's article Lua Performance Tips from the excellent [Lua Programming Gems] book is [available online].

Lua 4 Notes

The following information concerns optimization of Lua 4 and is kept here for historical reference.

General tips on coding

(Joshua Jensen) These are some optimization strategies I use (off the top of my head):

This information was written for Lua, pre v4.0 -- Nick Trout

Assertions

Using the standard assert function with a non-trivial message expression will negatively impact script performance. The reason is that the message expression is evaluated even when the assertion is true. For example in
assert(x <= x_max, "exceeded maximum ("..x_max..")")
regardless of the condition (which usually will be true), a float to string conversion and two concatenations will be performed. The following replacement uses printf-style message formatting and does not generate the message unless it is used:
function fast_assert(condition, ...)
    if not condition then
        if getn(arg) > 0 then
            assert(condition, call(format, arg))
        else
            assert(condition)
        end
    end
end
Now the example becomes:
fast_assert(x <= x_max, "exceeded maximum (%d)", x_max)

This is the VM code generated:

assert(x <= x_max, "exceeded maximum ("..x_max..")")
        GETGLOBAL  	0	; assert
        GETGLOBAL  	1	; x
        GETGLOBAL  	2	; x_max
        JMPLE      	1	; to 6
        PUSHNILJMP 	
        PUSHINT    	1
        PUSHSTRING 	3	; "exceeded maximum ("
        GETGLOBAL  	2	; x_max
        PUSHSTRING 	4	; ")"
        CONCAT     	3
        CALL       	0 0
fast_assert(x <= x_max, "exceeded maximum (%d)", x_max)
        GETGLOBAL  	5	; fast_assert
        GETGLOBAL  	1	; x
        GETGLOBAL  	2	; x_max
        JMPLE      	1	; to 17
        PUSHNILJMP 	
        PUSHINT    	1
        PUSHSTRING 	6	; "exceeded maximum (%d)"
        GETGLOBAL  	2	; x_max
        CALL       	0 0

Edit: April 23, 2012 By Sirmabus
The code above will not actually work with 5.1
Also added some enhancements like pointing back to the actual assert line number,
and a fall through in case the assertion msg arguments are wrong (using a "pcall()").

function fast_assert(condition, ...)
   if not condition then
      if next({...}) then
         local s,r = pcall(function (...) return(string.format(...)) end, ...)
         if s then
            error("assertion failed!: " .. r, 2)
         end
      end
      error("assertion failed!", 2)
   end
end

Fast Unordered List Iteration

Frequently in Lua we build a table of elements such as:
table = { "harold", "victoria", "margaret", "guthrie" } 

The "proper" way to iterate over this table is as follows:

for i=1, getn(table) do
    -- do something with table[i]
end

However if we aren't concerned about element order, the above iteration is slow. The first problem is that it calls getn(), which has order O(n) assuming as above that the "n" field has not been set. The second problem is that bytecode must be executed and a table lookup performed to access each element (that is, "table[i]").

A solution is to use a table iterator instead:

for x, element in pairs(table) do
    -- do something with element
end

The getn() call is eliminated as is the table lookup. The "x" is a dummy variable as the element index is normally not used in this case.

There is a caveat with this solution. If library functions tinsert() or tremove() are used on the table they will set the "n" field which would show up in our iteration.

An alternative is to employ the list iteration patch listed in LuaPowerPatches.

Table Access

Question: It's not the performance of creating the tables that I'm worried about, but rather all the accesses to the table contents.

(lhf) Tables are the central data structure in Lua. You shouldn't have to worry about table performance. A lot of effort is spent trying to make tables fast. For instance, there is a special opcode for a.x. See the difference between a.x and a[x] ... but, like you said, the difference here is essentially an extra GETGLOBAL.

a,c = {},"x"
        CREATETABLE	0
        PUSHSTRING 	2	; "x"
        SETGLOBAL  	1	; c
        SETGLOBAL  	0	; a
b=a.x
        GETGLOBAL  	0	; a
        GETDOTTED  	2	; x
        SETGLOBAL  	3	; b
b=a["x"]
        GETGLOBAL  	0	; a
        GETDOTTED  	2	; x
        SETGLOBAL  	3	; b
b=a[c]
        GETGLOBAL  	0	; a
        GETGLOBAL  	1	; c
        GETTABLE   	
        SETGLOBAL  	3	; b
        END


See also: VmMerge (used to format the merged Lua source and VM code), OptimisationTips , OptimisingUsingLocalVariables


RecentChanges · preferences
edit · history
Last edited April 24, 2012 7:42 am GMT (diff)