perf impact of passing references
JavaScript performance comparison
Info
This test is attempting to show the performance impact of passing references compared to normal scope chain lookups.
Check out Compact.js, that's based on Require.js
Dominik Guzei Wizzart by the way -> I'm searching for a JavaScript internship :)
Modified By
Preparation code
<script>
ui.benchmarks[0].setup = function() {
var Library = {
'util': {
'add': function(a, b) {
return a + b;
}
},
'cool': {
'Class': function(startValue) {
this.value = startValue;
}
}
};
Library.cool.Class.prototype = {
'addToValue': function(value) {
this.value = Library.util.add(this.value, value);
}
};
};
ui.benchmarks[1].setup = function() {
var cache = {};
function define(namespace, includes, callback) {
var i = -1;
if (arguments.length < 3) {
callback = includes;
includes = [];
} else {
includes = includes.slice(0);
}
// convert string namespaces to objects
while (includes[++i]) {
includes[i] = cache[includes[i]];
}
cache[namespace] = callback.apply(null, includes);
}
function require(includes, callback) {
var i = -1;
includes = includes.slice(0);
// convert string namespaces to objects
while (includes[++i]) {
includes[i] = cache[includes[i]];
}
callback.apply(null, includes);
}
define('Library.util.add', function() {
function add(a, b) {
return a + b;
}
return add;
});
define('Library.cool.Class', ['Library.util.add'], function(add) {
function Class(startValue) {
this.value = startValue;
}
Class.prototype = {
'addToValue': function(value) {
this.value = add(this.value, value);
}
};
return Class;
});
var Klass;
require(['Library.cool.Class'], function(Class) {
Klass = Class;
});
};
ui.benchmarks[2].setup = ui.benchmarks[1].setup;
</script>
Test runner
Warning! For accurate results, please disable Firebug before running the tests. (Why?)
Java applet disabled.
| Test | Ops/sec | |
|---|---|---|
Normal Lookup |
|
pending… |
Local Lookup |
|
pending… |
Passing+Require |
|
pending… |
Compare results of other browsers
Revisions
You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:
- Revision 1: published by Dominik Guzei and last updated
- Revision 2: published by Micheil Smith
- Revision 3: published by eric
- Revision 4: published by dnolen
- Revision 5: published by dnolen
- Revision 6: published by Jie
- Revision 7: published by Tom Schuster
- Revision 9: published by eric
- Revision 10: published by eric
- Revision 11: published by Dominik Guzei
- Revision 12: published and last updated
11 comments
This test shows that frameworks built on a global namespace (jQuery, YUI, Prototype, ...) could have been up to 98% faster (Firefox) if they were written in a string based modular fashion like it is used by Compact.js that is based on Require.js
Please tell me wrong, I can't believe it :-) Dominik Guzei Wizzart
I think you'll find that it's because you're deep-accessing on objects. If you move the defining of objects outside of the tests, they may well be faster.
Yes it would be faster and that's the whole point: Defining object chains is slow, and accessing them is even worse.
Both snippets to exactly the same, remember that! You can't move the defining of objects out of the test, because then you would be cheating in favor of the first snippet.
Lets have a look: eg. window.Library.cool is - load window object - load Library - load cool
you allocate a new object every time with {} already writing var lib = window.Library could already speed up the code vs. - load function - call function - set on closure var f
you are using the same object in the closure
Tom, good points -> I just did some corrections, is it ok now?
I did some improvements there: http://jsperf.com/global-namespace-chain-vs-string-based-local-modules/7
There is something funny going on for me. Running the test on the main page, I get results of 53/79 testing in Safari 5.0.5 on Mac OS X 10.6.7.
However, when I got to http://jsperf.com/global-namespace-chain-vs-string-based-local-modules/7 then the two tests are essentially the same speed for me, there is a 2-3% difference.
Actually, the defining of objects is a negligible difference, the real difference comes from deeply accessing variables. It's long bee known that it's faster to do something like:
compared to:
as you're not accessing the value for foo.length every time. Likewise, in your add() function, you've got a local reference to add, where as you're accessing three objects deep to get access to the add() function. The first code snippet actually out performs the second if you store a local reference to add(). Also, who actually writes their code using deep references like what you're using above? Most people probably store a few local references purely due to that "programmers are lazy" rule (aka, less typing)
True story :-D I just didn't think the difference would be so extrem!
Thanks for your comments and revisions, just learned a lot from you guys! I also rewrote the placative introduction and (intentionally) didn't change my (crappy) implementation, as a good example how important it is to keep one's locals around!
See you again, in another round :)
Sorry, I didn't see the comments panel and I commented your test directly on the revision 6! By caching the add function in a closure in the global test, both tests have the same speed.
I think if someone used the define function they would expect to still have the ability to manually reference that member in the normal notation (i.e. it would define the namespace in the global context). Using this requires you to use the require function if you want to use your modules (the ones created using the define function at least). You could make a case that it doesn't really matter if you're using a dependency manager because you still need to make sure your includes are loaded.. but personally, I'd still like the flexibility to access the modules using the existing standard.