Extreme optimization

I’ve been reading up on ways to optimize Lua and get the most out of performance.

The most common recommendation is “don’t”, but the second most common is “use locals”. Particularly this refers to having local pointers to functions.

Here I’ve made a simple test comparison of math.random. A note about the tests, Lua time stamps will only give us seconds as the smallest measurement. To compensate for this, I’ve made the test loops long enough to break into triple figures so we can see the timing differences better.

Also, these benefits are really only seen when running a call to these functions more than once. However it’s still a good thing to know I think.


-- Locals Performance Test
local random = math.random
local osTime = os.time
function setup()
    setupRandom = math.random
end
function draw()
    background(40, 40, 50)
end
function touched(touch)
    local startTime, endTime
    if touch.state == ENDED then
        print('started')
        startTime = osTime()
        for i = 1, 4000000000 do
            math.random(1, 1000000)
        end
        endTime = osTime()
        print('math.random', endTime - startTime)        
        local localRandom = math.random
        startTime = osTime()
        for i = 1, 4000000000 do
            localRandom(1, 1000000)
        end
        endTime = osTime()
        print('local', endTime - startTime)
        startTime = osTime()
        for i = 1, 4000000000 do
            setupRandom(1, 1000000)
        end
        endTime = osTime()
        print('setup', endTime - startTime)
        startTime = osTime()
        for i = 1, 4000000000 do
            random(1, 1000000)
        end
        endTime = osTime()
        print('global local', endTime - startTime)
        startTime = osTime()
        for i = 1, 4000000000 do
            otherRandom(1, 1000000)
        end
        endTime = osTime()
        print('other', endTime - startTime)
    end
end

Note - otherRandom is defined as a global in another file

Results (lower is better):
direct use of math.random - 120 seconds
define local in same scope - 97
defined in setup() - 107
defined local in global scope - 101
defined global in another file - 115

@skar Use socket to get time. Example below.

viewer.mode=STANDARD

function setup() 
    s=require("socket")
    st=s:gettime()
    for z=1,10000000 do
        a=math.sqrt(z)
    end
    en=s:gettime()
    print(en-st)
    
    ss=math.sqrt
    st=s:gettime()
    for z=1,10000000 do
        a=ss(z)
    end
    en=s:gettime()
    print(en-st)
    
    local ss=math.sqrt
    st=s:gettime()
    for z=1,10000000 do
        a=ss(z)
    end
    en=s:gettime()
    print(en-st)
end

Thanks a lot Dave. It’s strange, I came across using socket in different guides but it didn’t work for me, because I tried importing like this require “socket” not like this require(“socket”). Yet I’ve seen the former work in the Spine demo code, is the difference in local files vs system reference?

might be best to time an empty loop

adjusted test to use socket, added a couple more test scenarios