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: