lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

As expected, the __method metamethod was a measurable improvement over
using __index and creating closures.  But to my surprise, I had to
cheat quite a bit to get within 20% of the performance of using colon

I think you're actually selling the colon syntax short in these tests.  You've defined new_colon(t) using:

  return setmetatable(o, {__index = function(t, k)  return funcs[k] end, })

but, most people who use the colon syntax are probably skipping that extra level indirection by using the __index=table special case.  i.e:

  return setmetatable(o, {__index = funcs, })

In my own tests, switching to that syntax speeds up the colon-syntax numbers by about 25%.

I also think there's an important option that you're overlooking here.  I use implicit-closure creation to implement object methods in some of my own code, but, I take some care to avoid repeated creation of new closures.  It's still significantly slower than the colon syntax, but, much of the time, it's far better than just dropping down new closures anytime index is called (on my machine, the approach is about on par with the 'fast' method, assuming the garbage collector is turned off.)

The __method idea you're playing with feels like something that could be a useful hack -- but, I'm worried about some of the strange edge cases it would add to the language.  For example, it's unclear how to handle userdata that have definitions for both __index and __method.  If all you want is a version of the closure-style object syntax with relatively low overhead, I'd suggest looking at some version of lazy closure creation.  It's not as fast as the colon-syntax, but, it's also not nearly as slow as you've been assuming.


Here's a lazy implementation that's compatible with your tests:

local function new_lazy(t)

  -- only store cached member functions until the 
  -- gc gets around to freeing them.
  local member_functions=setmetatable({},{__mode="kv"})
  local mt = {}
  function mt.__index(t,k)
     -- return t[k], if it exists
    local var = rawget(t,k)
    if var then 
      return var 

    -- check the member function cache
    var = rawget(member_functions,k)
    if var then
      return var 

    -- create a new cached member function
    var = rawget(funcs,k) 
    if var and type(var)=='function' then
      local function self_fun(...)
        return var(t,...)
      return self_fun

    -- otherwise, behave as if __index=funcs
    return funcs[k]

  return setmetatable( t or {a = 0} ,mt)