24 comments

[ 5.0 ms ] story [ 49.9 ms ] thread
this is wrinkling my brain. why is it faster?
Object property lookups aren't free. The 'global namespace chain' code has to lookup the util property in the Library object, then the add function in the util object.

It's not particularly expensive, but if you do it in a tight loop 100k times it'll look slow compared to something that doesn't need to do that work.

As others have said, the trick is simply to perform those lookups earlier and assign the resulting function/data to a variable.

I love JavaScript!!

I have no idea how boring and where dynamic web-paging would be today without JS!

As ehynds (above) pointed out, the example could have been drastically simplified down to:

  window.Library = {};
  window.Library.util = {};
  window.Library.util.add = function(a, b) {
    return a + b;
  };
  var add = window.Library.util.add;
Then you will find that:

  add();
Will be faster than:

  window.Library.util.add()
It's unfortunate that JavaScript interpreters can't do something to fix this. Maybe Google's V8 JIT can speed this situation up.
I'm not sure it would be legal to fix up automatically. What is expected if you have a background timer which could change "window.Library.util" at some point? If you resolve every time, you'll get a new "add()", if you cache, you'll get the same.
You're right maybe it can't be done universally. But you could add an optimizer to a production build system which rewrites any instance of:

  window.Library.util.add()
To:

  var window_Library_util_add = window.Library.util.add;
  window_Library_util_add();
Are you saying that the V8 JIT won't catch this? What if it optimizes _any_ function/module/etc. that is later changed? The optimization should be discarded when the reference is reassigned. If it's not, then V8 has a bug that should be manifesting everywhere.
The problem is that the code is not semantically identical, there is no way for the compiler to know that nothing else has changed while the loop is executing. It's like the frequent optimization case of

  int len = array.length
  for(i = 0; i < len; i++){

  } 
vs.

  for(i = 0; i < array.length; i++){
  
  }
The code is semantically different and therefore has a different performance profile.
Depends what you mean by "changed". The function body itself cannot change in javascript afaik. The function bound to some name can change, but that's equivalent to the name pointing at another structure. The new structure will have its own compiled version of the new function. It doesn't mean though that it can just fold multiple resolutions of the same name into a local variable and cache it. This kind of behaviour would be similar to optimising random() to return 4.

I'm not sure if v8 does any whole-module optimisations... if it does, then there's got to be loads of "invalidate me" hooks everywhere. I doubt it would be worth doing, but if anyone has more precise info - please correct me :)

This appears to be micro-benchmarking the result of incrementing an integer by calling a function that calls a function nested in a bunch of object properties, vs. the same, but the called function lives in a closure.

Moral of the story? Perhaps, "make local aliases for objects used in performance-critical loops." Nothing new here.

Certainly not a good argument for one style of JavaScript library over the other (which seems to be the aim).

What's really unfortunate is that this appears to be deliberately misleading. The writer of the test is either acting maliciously or doesn't have nearly enough understanding of how Javascript works to be making such an assertion.

Sad to see this kind of silliness on HN.

Could you be specific about what was deliberately misleading? You've made the assertion but didn't specify what you were referring to. Sad to see that kind of silliness on HN.
It's deliberately misleading to say that jQuery and other libraries use a pattern where (internal to the library) there are constant, deep chain lookups. The entire "problem" can be easily "fixed" with the solution above--cache local references so you're not doing deep-lookups constantly (which, if you look at the source of the referenced libraries, you won't find).

As I said; it's either deliberately misleading--or the author's understanding of javascript is too limited as to be making claims that "jQuery and other libraries could have been 98% faster." A comment, by the way, which was later modified to state "if you are not a JavaScript Ninja your framework code could have been up to 98% faster."

Sorry, I don't file that knowledge under "Javascript Ninja" knowledge--and moreover, the original comment was designed to say, "My library is faster / better than all of these popular libraries."

[Edit] To my (admittedly cynical at times) that adds up to deliberately misleading.

Hey there, I changed the introduction on jsperf to reflect the awesome solutions that were posted afterwards!

nevertheless it shows that if you don't keep those rules in mind all the time you might end up with very slow code.

Thanks for your comment though!

In the future you might consider only putting down words that you're willing to stand behind.
If you read the comments, it turns out that he's not really aiming to show anything, just experimenting. It's just interesting to know quite how severe the costs of global namespace lookups can be, for me.
The original description of the test had other implications--implications which have now been removed.
That's why you cache methods to reduce lookups.

http://jsperf.com/global-namespace-chain-vs-string-based-loc...

In your tests the method "add" is pretty far away from it's original variable, and MUCH further away than any jQuery method is, so you're also testing the time it takes to lookup that method. In jQuery, methods are either attached to "jQuery" directly or on the prototype.

Doesn't Google Compiler do this for you?
That's (part of) the premise of the "myjs" library demoed at PariJS last month too - http://myjs.fr/ - get rid of scope chain access and speed things up.
(comment deleted)
What's the difference between writing myLibrary.foobar and myLibrary_foobar? Everyone got too caught up with Java-style module naming convention to bother to figure out how JavaScript actually worked (or to notice that the Java compiler flattens the package hierarchy).
So, uh... I ran this on safari in iOS 4.1, the deep lookup (1st row) came out fastest. That's not what's supposed to happen, right?