Lua optimization

Just out of curiosity, since I am not a lua expert.

Is this fragment

    local x
    for i = 1, 1000 do
        x = ...something...
    end

faster then this

    for i = 1, 1000 do
        local x = ...something...
    end

My apologize for the (most probably) silly question…

1 Like

It is just as easy to test by getting the time before and after the loops.

I think yes, there’s a lot of create & release variables in the second one (just my guess)

On my system the following

    local y
	local x
	for i = 1, 10000000 do
		x = i*i / 1000.0
		y = y + x
	end

is 0.5 millisecond faster then the version with local inside the loop. I measured the time with socket.gettime. Note that 0.1 millisecond is the precision of socket.gettime.

Conclusion: the time difference is 0.5 * 10^{-7} per loop cycle. I would say it is negligible for any reasonable use of lua as a scripting language.

3 Likes

In this case, the local variable is being created and destroyed with each pass through the loop, correct? It seems to me that this is not good.

The test say the difference is sooooo small :slight_smile:

It doesn’t matter. The time difference is insignificant.

I ran the following functions a million times, in alternating passes

local function empty()  end

local function upvalue()
	local x
	for i = 1, 1000 do
		x = i
	end
end

local function localvalue()
	for i = 1, 1000 do
		local x = i
	end
end

The times (in seconds) for each, summed up:

Total Iterations: 1000000
tests for ‘empty’ run in: 0.0099935930000018
tests for ‘localvalue’ run in: 0.280853607
tests for ‘upvalue’ run in: 0.281357203

The relative difference is only a fraction of a percent, in a million calls, and also far less than simply calling an empty function for the same number of times.
There is no difference.

3 Likes

In my experience, with LuaJIT, the only general “trick” that makes a difference in performance is “localizing” functions and other values to avoid repeated table accesses, for things that you are using repeatedly.

For example if your loop used a math function, set it to a local variable first, or if you’re accessing the same property or sub-property of a table in a loop, put it in a local variable first instead of accessing it repeatedly

local max = math.max

local function doSomething()
	for i=1,1000 do
		local v = max(...)
	end
end

local function doSomethingWithObj(obj)
	-- Localize here instead of accessing repeatedly inside the loop.
	local prop = obj.thing.property
	for i=1,1000 do
		-- Do something with `prop`...
	end
end

There are also some differences in cost between various built-in functions that may not be obvious of course, though unless you’re using them thousands of times it won’t matter. Otherwise, LuaJIT does all the optimization for you and makes abstractions free.
Regular Lua is much slower than LuaJIT as baseline, and also does not get the “no-cost abstraction” that LuaJIT gives you, but you would have to test specific code against alternatives to know what’s costly and what’s not.

5 Likes

some quick tips for lua optimization (based on my experience with non-JIT:

  • use locals
  • avoid excessive definitions
  • avoid string manipulation
  • don’t use ipairs
  • avoid redundant function calls

as for your specific question, it’s best answered by looking at the compiled bytecode (compiled with lua 5.2). the first approach nets:

LOADNIL
LOADK
LOADK
LOADK
FORPREP
...
FORLOOP

while the second appoach nets

LOADK
LOADK
LOADK
FORPREP
...
FORLOOP

you can see there’s very little difference. performance wise they’d be nearly identical, but it really depends on your variable scope needs. If you need the variable x outside of the loop, then you can’t set it local inside.

1 Like