[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: questions about closure construction
- From: Ross Bencina <rossb-lists@...>
- Date: Tue, 19 Mar 2013 13:27:40 +1100
Hi Everyone,
I have some questions about function/closure construction overhead and
how to most efficiently define OO "member functions". I am working on
some micro-benchmarks to help inform my decisions but so far the results
have been inconclusive so I'd like to ask some questions.
Ultimately these questions are about me trying to get a better intuition
about how to write efficient Lua code. I've given more background below
if you're interested, but the questions stand alone.
<background>
I'm trying to wrap my head around the best way to implement some OO
class constructor functionality for small classes.
In my situation there are many different "subclasses" but each object
instance has only 3 member methods and a few instance variables. Often 2
of the methods are no-ops. Sometimes there is no class state, or it is
used only by one method. Sometimes the methods execute lambdas passed as
constructor parameters. So I am considering when and how to use closures
vs metatables vs assigning the methods directly into each instance table.
Sometimes it seems like it would be most concise to declare all the
functions inline in my constructor, but I fear this will be more
expensive than declaring member functions up-front globally, or in a
global table and then assigning them in to each instance.
I have around 100 of these classes to implement so I'd prefer to work
out the correct implementation approach before I start.
In the end, object method execution speed is more important that
construction speed.
</background>
So now the specific questions:
1.
Aside from the visibility of __y_bar below, do the two functions X, Y do
the same thing? or do X (and Z) construct a new closure every time they
are called? (even though their inner functions don't capture any
variables from the enclosing scope)
function X()
return { foo = function(self) print "x" end }
end
X():foo()
function __y_bar(self) print "y" end
function Y()
return { foo = __y_bar }
end
Y():foo()
I also tried this:
function Z()
local function __z_bar(self) print "z" end
return { foo = __z_bar }
end
Z():foo()
Z runs slower than X or Y, suggesting that there is some runtime
overhead in creating a named local variable (which surprises me, seems
like a simple optimisation).
Does the ''function() print "z" end'' bit get evaluated every time Z is
called? or is it "precompiled" so that assigning a local function like
this is no different that assigning from the global scope as in:
local __z_bar = math.sin
1a.
Can I assume that the following generate exactly the same code? or are
they subtly different?
r = {}
function r:foo() end
r = { foo = function(self) end }
local function _foo( self ) end
r = { foo = _foo }
-- no further use of _foo
local _foo = function(self) end
r = { foo = _foo }
-- no further use of _foo
2.
Another question is about capturing "instance state". Consider the
constructors below. Assuming that my instance state will be only used by
a single method foo(), would I be better off storing it in the self
table, or capturing it with a closure? Are there space/time trade offs
involved? What are they?
-- A: state x captured in closure
function ClassA( x )
return { foo = function(self) print(x) end }
end
a = ClassA(1)
a:foo()
-- B: state x stored in object field,
-- function defined inline in every call to constructor
function ClassB( x )
return { x_ = x,
foo = function(self) print(self.x_) end }
end
b = ClassB(1)
b:foo()
Initial tests suggest that a:foo() is a little faster, and the ClassB()
constructor execution is slower, presumably because it's more expensive
to add a table entry ( x_ in ClassB() ) than capturing an upvalue into a
closure (x in ClassA()).
I also wonder about memory usage: these are small "objects". The tables
will usually have 3-6 fields. Is the memory overhead likely to be
greater capturing a few upvalues in a closure, or using additional table
fields?
I'm also considering using the standard "class object metatable"
approach. I'm considering other options because I don't think metatable
will be as concise, and sometimes I may want to construct per-instance
closures with state (as above).
Sorry for the long message. Thanks for your attention.
Ross.