Performance Concerns for Nested JavaScript Functions

Since I dabbled quite a bit in functional languages like Haskell, I came to like nested functions very much.

Searching on the net, I came across many posts and stackoverflow answers claiming that this has a performance impact since a function nested inside another has to be recreated every time the outer function is called.

After talking to my colleagues who assured me that this is not true, at least not for nodejs and chrome (both of which use the v8 JavaScript engine), I decided to find out for myself.

Simple Test

(source on github)

var calls = 99999999;

function notNested() {
    var start = new Date().getTime();

    function foo() { return 0; }

    function bar () { foo(); }

    for (var i = 0; i < calls; i++) {
        bar();
    }

    console.log('Unnested took %s ticks', new Date().getTime() - start);
}

function nested () {
    var start = new Date().getTime();

    function bar () {
        function foo() { return 0; }
        foo();
    }

    for (var i = 0; i < calls; i++) {
        bar();
    }

    console.log('Nested took %s ticks', new Date().getTime() - start);
}

function nestedReturning () {
    var start = new Date().getTime();

    var bar = (function () {
        function foo() { return 0; }

        return function () { foo(); };

    })();

    for (var i = 0; i < calls; i++) {
        bar();
    }

    console.log('Nested returning took %s ticks', new Date().getTime() - start);
}

notNested();

nested();

nestedReturning();

(Please keep in mind that this is in no way intended to be a proper performance test, but merely a sanity check)

Results:

Running this with nodejs yields the following result:

➝  node nested-functions.js 
Unnested took 1606 ticks
Nested took 2316 ticks
Nested returning took 1614 ticks

Here we can see, in this case we can see that nesting a function causes code to run about 1.4x as slow, while ensuring that the outer function is only created once by returning it is as performant as not nesting.

Somewhat of a difference there, but what about the different browsers?

In order to make testing these easier and thanks to Charlie Robbin’s advice (see comments), I went to jsperf.com and created a performance test

For people better with visualizing numbers:

(shows operations per second e.g., higher is better)

If you use a browser that’s not listed yet, head on over to see how your browser is doing.

As this data shows, there is a considerable impact when “carelessly” nesting functions.

How considerable depends on the browser (e.g., Firefox and IE seem to punish us the most). Chrome  handles them best and suffers only about 30% ,

Returning the outer function as an object and thus ensuring it only gets created once , pretty much fixes the performance hit in all browsers.

Conclusions:

  • nest functions, but do so wisely (e.g., as outlined in the ‘nestedReturning’ example)
  • be aware however that this pattern will increase the memory footprint of your application since the returned function closes over the inner one and thus prevents it from being garbage collected
  • if you are looking for the lowest footprint, highest performance option and can live with slightly less nicely structured code, declare functions at outer scope as much as possible

Note: make sure to use the revision 3 of above performance tests, as the initial ones were faulty and tested how lazy browsers evaluate functions instead of what they were supposed to.

Advertisements
  1. #1 by Charlie Robbins on June 2, 2012 - 6:50 pm

    Interesting analysis of your hypothesis here, but what about other Javascript engines? You should make a page for this on JSPerf: http://jsperf.com/

    • #2 by Thorsten Lorenz on June 2, 2012 - 7:38 pm

      Good idea, I created a jsperf slug here.
      People can now head over there and test in different browsers.

    • #3 by Thorsten Lorenz on June 2, 2012 - 7:55 pm

      For unknown reasons the jsperf results (especially for safari) contradict what I found when running these just in the browser console.

      To elaborate, in the console chrome clearly beat safari for nested functions and safari performed almost 3x worse when functions where nested vs when they weren’t nested.
      Safari beat chrome for unnested functions (1.3x).

      On jsperf safari beats chrome in both cases (about 1.3x faster) and there is seemingly no difference between nesting vs. not nesting functions for safari (actually nested functions perform slightly faster which is puzzling).

      I ruled out the possibility that Safari just does things differently in the browser than in the console, by creating an html page that runs this script when opened in the browser (available with the source).
      Safari behaves exactly the same as in the console.

      Chrome however becomes 3x faster – I guess this is since in the console chrome runs things in the background in order to keep the console responsive, which naturally comes at a cost.

    • #4 by Thorsten Lorenz on June 3, 2012 - 12:06 am

      Realized that my tests were off and fixed it in jsperf (updated blog as well). Now the results I’m getting make more sense to me (they are a lot different from the ones I got before).

  2. #5 by Alex on June 14, 2012 - 5:18 pm

    Interesting that your performance tests don’t have a huge spread. I tested in my Chrome and this is what I got both times:

    Unnested took 124 ticks
    Nested took 37033 ticks
    Nested returning took 122 ticks

    Where nested took 300 times more ticks.

    In any case, very useful info. Thanks.

    • #6 by Thorsten Lorenz on June 14, 2012 - 6:29 pm

      You are right!

      The tests were faulty. I fixed them here: performance tests.
      The new results are more aligned with your findings (except for the nested returning scenario).
      I updated this post accordingly.

      Thanks for pointing it out.

  3. #7 by binary options best on August 6, 2013 - 9:17 pm

    Hello There. I found your blog using msn. This is an extremely well written article.

    I’ll be sure to bookmark it and come back to read more of your useful info. Thanks for the post. I’ll definitely comeback.

  1. Performance Concerns for Nested JavaScript Functions » Lab49 Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: