Re: How come simple "closures" aren't compiled?
- From: Javier Guerra Giraldez <javier@xxxxxxxxxxx>
- To: LuaJIT <luajit@xxxxxxxxxxxxx>
- Date: Mon, 5 Dec 2016 09:09:19 +0000
On 4 December 2016 at 19:58, Luke Gorrie <luke@xxxxxxxx> wrote:
How about if you would replace the original code:
foreach(t, function(i, n) return n * 2 end)
with an alternative:
foreach(t, fn[[b*2]])
that does JIT efficiently and does not create new closures. Could be
implemented as:
-- fn(expr): Create a closure that returns the value of <expr>.
-- expr is a Lua expression with up to five arguments (a, b, c, d,
e).
--
-- Example: fn[[a*b]](21,2) => 42
memo = {} -- memoization table
function fn (expr)
if memo[expr] == nil then
local code = ("return function (a,b,c,d,e) return %s
end"):format(expr)
memo[expr] = assert(loadstring(code))()
end
return memo[expr]
end
-- Example:
local acc = 0
for i = 1, 100 do
acc = acc + fn[[a*b]](21,2)
end
assert(acc == 42*100)
This is a very useful strategy; which helps a lot in writing nice
abstractions. One nitpick: sometimes, when the 'fn()' function is
called repeatedly with different arguments (for example, loading a
hundred user-defined expressions at load time), the JIT sees it as not
very compilable and "blacklists" it. A workaround is to isolate the
slow path from the fast one:
--------
local memo = {} -- memoization table
local function compile (expr)
local code = ("return function (a,b,c,d,e) return %s end"):format(expr)
memo[expr] = assert(loadstring(code))()
return memo[expr]
end
function fn (expr)
return memo[expr] or compile(expr)
end
--------
--
Javier
Other related posts: