• Subject: [Benchmark] Chain calls
• Date: Sat, 15 Nov 2008 14:46:45 +0300

Hi, list!

Today I'm benchmarking chaining function calls. That is, chained foo() () () vs. plain foo() foo() foo() form. The benchmarked code is attached.

The benchmark results:

lua
-------------------------------------------------------------------
name |     rel | abs s / iter = µs (1e-6 s) / iter
-------------------------------------------------------------------
plain |  1.0000 |  82.31 /  100000000 = 0.823100 µs
chain |  1.0095 |  83.09 /  100000000 = 0.830900 µs
plain_chain |  1.0884 |  89.59 /  100000000 = 0.895900 µs
luajit -O
-------------------------------------------------------------------
name |     rel | abs s / iter = µs (1e-6 s) / iter
-------------------------------------------------------------------
plain |  1.0000 |  12.68 /  100000000 = 0.126800 µs
chain |  1.0899 |  13.82 /  100000000 = 0.138200 µs
plain_chain |  1.2011 |  15.23 /  100000000 = 0.152300 µs

The "chain" and "plain_chain" cases use a function which returns itself:

local function chain() return chain end

bench.chain = function()
chain () () () () () () () () () () -- 10 calls
end

bench.plain_chain = function()
chain ()
chain ()
-- ...
chain () -- 10 calls
end

And "plain" case uses a do-nothing noop function call.

local function plain() end
bench.plain = function()
plain ()
plain ()
-- ...
plain () -- 10 calls
end

In plain Lua, "plain" and "chain" cases are almost of the same cost. OTOH, the "plain_chain" method with its "wasted" return is noticeably slower. It is somewhat unexpected for me. (Note however that we're talking of very small numbers, both relative and absolute.)

I guess that the cost of returning function is compensated by proper tail recursion in the "chain" case, and that x1.09 slowdown of the "plain_chain" case is that cost itself.

Is my naïve analysis correct?

Alexander.

Attachment: chaincallbench.lua
Description: Binary data