Optimising Lua code through using local functions

Viewed 289

So, I know that assigning global functions to local variables makes them run significantly faster, like writing code:

local _cos = math.cos
for i = 1, 100000 do
    _cos(i)
end

Is much faster than code without first localizing the function:

for i = 1, 100000 do
    math.cos(i)
end

However, based on my tests that doesn't seem to work with things like tonumber, type or pairs.

Could someone clarify why some global functions get significantly faster and others don't seem to be affected at all?

1 Answers

Localizing global functions doesn't make them significantly faster. It makes them a bit faster, because you don't have to look them up in _ENV; specially if they're in another table like _ENV.math.cos. For functions that are directly in _ENV this effect is obviously smaller, and if the function itself runs very slow, the tiny difference of localizing it won't be as noticeable.

In fact, you should never localize functions just because. If you have a very small loop body with lots of iterations that calls a very small function, then you should consider localizing it (and you should do that as close to the loop as possible), but only if you're sure this will make a difference (benchmarking is your friend).

There seems to be this persistent myth that just localizing every function you're going to use to the entire library will magically make it faster, which is a total misconception.

LuaJIT even does this itself for most cases; detecting repeated table access within a loop body and localizing it outside the loop.

Also keep in mind that, while math.cos shouldn't ever be changed, you might be accessing a function from some other module that actually changes behind the scenes to reflect some state-change in the module. In that case, localizing the function will mean you're working with an outdated function. This isn't very common, but it does happen.


Just for fun; try running this in PUC Lua and in LuaJIT:

local function bench(n, fn, ...)
    local t1 = os.clock()
    for i=1,n do
        fn(...)
    end
    local t2 = os.clock()
    return t2 - t1
end

local n = 1e7

print(bench(n, function() return math.cos(20) end))
print(bench(n, function() return tostring(20) end))
print(bench(n, function() return tonumber("20") end))
print("---------")
print(bench(n, math.cos, 20))
print(bench(n, tostring, 20))
print(bench(n, tonumber, "20"))
Related