Web Page Global Variable Performance

By on August 10, 2009 12:02 am

Google recently released a new feature for their Google Docs writing application; the ability to draw a vector graphic and insert it in your document. This drawing module uses SVG and VML, much like DojoX GFX. I was curious about their code and explored with Firebug. To my surprise, I found that most of their code uses variables in the global scope. The code is compressed, but when observing with Firebug’s DOM tab, there are several pages and a few hundred lines of global variables.

DocFirebug

This surprised me. I had always read that working in the global scope was slower than working in a new object and that when building web applications and looking for maximum performance, you should avoid window.myVar and opt for myObject.myVar. Technically these terms would be a variable for a value set at the window level, and a property for a value set at the object level. For the purposes of this test, I’ll be referring to them as globals and locals.

So the basic idea is to conduct a test where I get and set variables in the global and local scope and time them.

The Test Code

First I made variables to be used in the global and local objects. They were designed to be very random, to offset any possibility of sequential look-ups. I ended up using 25,000 variables, because it was enough to give a decent comparison range, and not enough that it would cause memory errors in Internet Explorer 8. This step takes time obviously, but since this would be more about loop and Array performance, it is not relevant and not included in the test times.

v = [];
makeVars = function(amt){
	for(var i=0;i

The next step is to set the locals in a fresh object, and set the globals in the window. All variables are set to a simple boolean of true:

obj = {};
setLocals = function(){
	for(var i=0;i

Next is the code to access the variables:

getGlobals = function(){
	for(var i=0;i

Now we can execute our functions wrapped in timers. I'm using a custom-written timer, since we will be testing in browsers that are not equipped with Firebug:

timer.start("l");
setLocals();
var lSet = timer.end("l");

timer.start("g");
setGlobals();
var gSet = timer.end("g");

timer.start("gl");
getLocals();
var lGet = timer.end("gl");

timer.start("gg");
getGlobals();
var gGet = timer.end("gg");

The custom-written code actually has a lot to it, including logging, averaging, and storing results, but I'm not going into that here.

I then ran this test ten times and took averages of each for all the major browsers. The results:

[TABLE=5]

For a change, we are not comparing the browsers to each other. We know Internet Explorer is slow (although surprisingly, it beats Firefox in the globals test), and we know that Safari 4.0 is going to be an insane aberration of web technology what we expect.

Focusing on Firefox 3.5 in the first column, we see that everything we've read is true. Setting the globals takes 285 milliseconds, while setting the locals takes 24 milliseconds. Setting locals in Firefox is ten times faster than globals, and getting locals is over eight times faster.

What's interesting, even though browser comparisons are not the point, is that Firefox is slower than all other browsers except Internet Explorer 6 – and Firefox 3.5 is even slower than Firefox 3.0 for this test. This is possibly because I haven't optimized for Tracemonkey, but Firefox does not perform as well as expected. Regardless, the test is consistent with the expected results: globals are slower than locals in all browsers. Except for Safari, where the difference is insignificant.

Analysis

The following are charts for Firefox and Internet Explorer. There is an additional bit of data called Complex Object. Here I create an object within an object within an object 25,000 times. This was the second most challenging thing for Firefox and the most challenging thing for Internet Explorer.

IE Chart

You'll notice the spike in Firefox at around 19,000 objects. This test was run ten times and averaged, so I assume this is where the Garbage Collector (GC) kicks in:

Firefox Chart

Why Firefox 3.5 is slower than version 3 and slower than Internet Explorer is not immediately clear. One answer may be in XPCOM, the development framework that Mozilla uses which is similar to Microsoft's COM. Scott Collins, one of the original software engineers at Mozilla and Netscape was largely responsible for the authoring and implementation of XPCOM and laments the fact that while it's a good framework, it was overly utilized and a cause for poor memory management and code bloat. In fact, Mozilla looks to be moving away from XPCOM as of Firefox 4.0

So in theory, the window space should run faster because there are fewer scope and prototype chains to climb. But this entirely depends upon the browser implementation. The window object is more than just JavaScript, it's also the interface to the browser engine.

Notes

I've been burned by Firebug before, so I uninstalled it and reran the test. The results were unchanged.

I also tried running the test ten times in a row within the same page to see if this would allow Tracemonkey to identify and compile the test as "hot code". It had no effect.

Conclusion

Small web pages with several global functions and variables would not experience a noticeable affect. Web applications would probably not have a noticeable impact neither, but since the results are indisputable that the global space is slower, its curious that the performance conscious Google chose to deliver this code. The results are not staggering enough to cause anyone to change their coding habits, but they show a good argument for form following function. Working in local objects is not only a good idea for code organization and maintenance, it will perform better too.

Another lesson to be learned is that even in Safari, setting variables always takes longer than getting them. This may be obvious computationally, in that there are extra cycles involved in creating that variable and finding a memory location for it. But it's something to keep in mind while writing our web applications. Writing always takes longer than reading.

Teaser

I made an unexpected discovery while conducting the tests. In my next post, I'll show more data in regard to page unload performance.

Comments

  • Very interesting Mike but I’d like to know how many vars a very very complex web application sets on average. I know I’m using global vars for several things myself but I doubt they amount more than ten, twenty at worst. Nothing to really worry about

  • I’m also curious whether it makes any difference if you set the variables by doing either of the following in the global environment:

    var FOO = true;
    // or, alternatively:
    FOO = true;

    and then looking them up without prefixing them with window? For sure, if I create:

    function foo() { /* … */ }

    I do not call it by doing:

    window.foo();

    I just do:

    foo();

    Arguably, it should not make a difference, but these are web browsers, so stranger things have happened. This better models how globals are used in practice, so I’m curious whether it affects the benchmarks.

  • David

    It seems like it is all the rage to wrap things in a closure and use local variables (not obj.foo, but var foo in an anon function) but I’d be interested to see how those stack up in this comparison. Any chance you can insert that into the data set?

  • @Michael: You’re right — the way these fields are defined and accessed makes a significant difference.

    @David: Referencing locals via closure is almost always slower than either of these two approaches.

    I wrote up a bit more analysis here: http://blog.j15r.com/2009/08/where-should-i-define-javascript.html

  • @Joel
    Nice blog and well written. I think we were going for slightly different goals, and that makes your tests very complimentary to mine.

    I attempted to emulate you tests. I only ran this on FF3.5 because that was the one with “issues”. I still got results that were in line with my original tests. I did 5000 vars because doing more was just getting a little crazy.

    One thing to note, and I’m pretty sure neither of us did, is that the JS engine may possibly resolve those variables in the code block before the code is executed. Keeping them in functions (and not in the global scope) should prevent that.

    Here are my tests and results. Interesting that setting vars took 0ms. I think that may be because of my statement above.

    setGVars = function(){
    a0=0;a1=1;a2=2;a3=3;a4=4; …. 5000
    }
    setLVars = function(){
    var a0=0;var a1=1;var a2=2;var a3=3;var a4=4; … 5000
    }
    setProps = function(){
    var o = {};
    obj.a0=0;obj.a1=1;obj.a2=2;obj.a3=3;obj.a4=4; … 5000
    }

    timer.start(“set”);
    setGVars();
    var gSet = timer.end(“set”);

    timer.start(“lset”);
    setLVars();
    var lSet = timer.end(“lset”);

    timer.start(“pset”);
    setProps();
    var pSet = timer.end(“pset”);

    set globals:15ms
    set vars:0ms
    set props:4ms

  • @Jose
    Google is using 1500 globals. A window with no code has around 70. I believe Google is on the cusp of a performance hit (in Firefox anyway).

    In IE *any* type of variable or property affects its performance because of its anemic garbage collection. I’m working on more tests on that, though it’s tricky to come up with both something that is a real world case and a unique perspective not demonstrated before.

  • Are the code snippets complete? Because doing

    obj = {};

    will create a global variable that can be accessed either via window.obj or just obj. To have a real local variable you need to do

    var obj = {};

    If you don’t use the latter, you actually measure the difference between “obj” and “window.obj”, but not between variables in the local and global scope.

  • Pingback: SitePen Blog » Hacking Safari’s Inspector()

  • @Mike:

    I know it’s been over a month since you posted this, but I’m just now realizing that you responded to my concerns above. Sorry it’s taken so long, but I posted some detailed clarification here:
    http://blog.j15r.com/2009/09/javascript-variables-continued.html

    Hopefully we can all get to the bottom of what’s going on here, so that we don’t all find ourselves paying an unnecessary performance penalty at runtime!