Wednesday, March 4, 2009

Closures VS. Properties / arguments.callee is expensive

I did some quick benchmarking to test whether it makes a difference to substitute a closure with a function that has a property (sometimes called inside-out-objects). Both are basically the same thing, only that the function property is mutable from the outside while access to the bound lexical variables of the closure is private to the closure.

Here is the source for two simple "getter-makers" that use the different strategies:
var makeGetterProperty = function (name) {
var func = function () {
return this[arguments.callee.__prop_name__]
}
func.__prop_name__ = name;
return func
}

var makeGetterClosure = function (name) {
return function () {
return this[name]
}
}
I timed both execution time of the "make" functions and of the actual getters. Making the getters is almost equally fast in both version; however executing the actual getters differs by a rate of about 150. The closure variant is significantly faster. (All these numbers are only worth their money in FF3.0.x on OSX). Interestingly turning the property variant into a closure that references itself to access the name of the property makes them both almost equally fast.

Conclusion: If you need to build inside-out-objects without closures (e.g. because you need to serialize the function state) that use self-referencing functions instead of accessing arguments.callee.

3 comments:

Anonymous said...

Indeed arguments.callee is expensive because it requires walking the stack, but just referencing the arguments object takes quite a large amount of time, in Nitro and V8 decisions are made base on whether the arguments object is referenced in code, and referencing the arguments object (at least for stack introspection) will cause TraceMonkey to abort (can't inline code that expects a callframe).

You're right that you are far better off to just reference the function, happily JS does allow named function expressions, eg.

var myFunction = function foo() { foo() }

will recurse infinitely, better yet the name 'foo' is not injected into the parent scope, and is immutable, so allowing (technically) a large number of optimisations to be performed.

Malte said...

Very interesting! I'm "naming" "anonymous" functions all the time to get better stck traces but I didn't know that the name could be used inside the function.

Thanks, I'll probably use this idiom a lot in the future.

peterS. said...

hallo Malte,


sorry for disappointing you; unfortunately, and once again due to msie,
this is not a solution ones code should last on - pasting the following
example into jconsole.com will clearly point out the differences between
microsofts implementation and the ones of all other major browser brands.

but even if dropping msie, one should be aware of the functions name-
scoping as can be comprehended with the 3 different occurences of [foo]:

the [foo] labeled function expression referenced to [bar] will not be
able to refer to any other [foo] labeled/referenced function - though
using this approach of labeling function expressions might make one
getting stuck to a pretty strict naming-policy, loosing the more
liberal ability of recycling/reusing variable names.


example:


function foo () {

print("global [foo]");
}


(function () {


function foo () {

print("[foo] embedded into an anonymus application context");
print("\n");
}
var bar = (function foo () {

print("(foo === arguments.callee) ? " + (foo === arguments.callee));
print("(arguments.callee === bar) ? " + (arguments.callee === bar));
print("(foo === bar) ? " + (foo === bar));
print("\n");

print(foo);
print("\n");

//foo(); DON'T - NEVER EVER

print(window.foo);
print("\n");
});
var baz = (function biz () {

print("(biz === arguments.callee) ? " + (biz === arguments.callee));
print("(arguments.callee === baz) ? " + (arguments.callee === baz));
print("(biz === baz) ? " + (biz === baz));
print("\n");
});

baz();

bar();
foo(); // the embedded and not the [foo] labeled one function expression.

window.foo();


})();