4

I have a quite complicated question to ask :)

I am currently working on a html5 canvas game. The variables which are specific to a map of the game are in a separate file (let's call it game.js), separated from the game engine (let's call it engine.js).

I have read that global variables are slower to use in JS than local variables. Therefore, in game.js, I create a global variable which contains all the game-specific variables. In engine.js, I copy this global object to local variables, there I delete this global object.

This is working. But I know that assigning objects only pass a reference to these objects.

Therefore my question is: will the performance of that be as if I had declared all the variables directly as local variables in engine.js, as I delete the global object at the end of the initialisation, or will it be slower, as if my local variables in engine.js were just references to a global object?

I could declare all the variables as local in engine.js, but it would be useful for me to separate what is specific to the map, if later I want to make other maps/games.

For example:

game.js:

Game = function() {
this.x = 16;
this.y = 16;
this.myObject = {"test": "hello", "action": "none"};
}
game = new Game();

engine.js: //...

var x = game.x;
var y = game.y;
var obj = {"test": game.myObject.test, "action": game.myObject.action};

//...

In this example, will the performance of x, y and obj be as fast as local variables, or slower?

Note: I didn't really check the difference between performances of global vars, and local vars, but I assume what I read about it is right.

Hope my question was clear enough and not silly :) If you have any ideas... Thanks!

Jo Pango
  • 474
  • 1
  • 5
  • 15
  • Don't worry about it. The performance difference is miniscule. – SLaks Oct 04 '11 at 15:22
  • Why don't you simply instantiate `Game` in engine.js? Either way, you don't need to delete the global object to get this performance gain. See my answer for details. – benekastah Oct 04 '11 at 15:59

2 Answers2

2

In modern browsers, there probably isn't much performance difference between local and global variables.

However there is an issue with memory consumption - global variables persist as long as the page stays open whereas local variables are garbage collected after control leaves their scope. Using local variables reduces the chance of having a memory leak.

Update

The long-winded discussion in the comment thread prompted me to write a test script to measure execution speed differences contrasting global and local scope access.

Initially there seemed to be no difference and results vary with no specific preference towards one or the other. However @DaveNewton provided a counterexample which consistently shows better performance for local variables by an order of magnitude.


Firefox 7

Milliseconds used for 20000 global accesses: 132

Milliseconds used for 20000 local accesses: 159

Internet Explorer 9

Milliseconds used for 20000 global accesses: 1750

Milliseconds used for 20000 local accesses: 1699

Google Chrome 14

Milliseconds used for 20000 global accesses: 46

Milliseconds used for 20000 local accesses: 55

Test script itself

<html>
<head>
<script type="text/javascript">

function t() {

var test = function () {}
test.issue = new String("hello");

var start = new Date().getTime();
(function() {
    (function() {
        (function() {
            (function() {
                (function() {
                    (function() {
                        (function() {
                            var a = document.getElementById("a");
                            for (var i = 0; i < 20000; i++) {
                                a.innerHTML = test.issue.toString();
                            }
                            a = null;
                        })();
                    })();
                })();
            })();
        })();
    })();
})();
var stop = new Date().getTime();
document.getElementById("a").innerHTML = "Milliseconds used for 20000 global accesses: " + (stop - start);


var start = new Date().getTime();
(function() {
    (function() {
        (function() {
            (function() {
                (function() {
                    (function() {
                        (function() {
                            var c = document.getElementById("c");
                            var testx = {};
                            testx.issue = new String("hello");
                            for (var i = 0; i < 20000; i++) {
                                c.innerHTML = testx.issue.toString();
                            }
                            c = null;
                        })();
                    })();
                })();
            })();
        })();
    })();
})();
var stop = new Date().getTime();
document.getElementById("c").innerHTML = "Milliseconds used for 20000 local accesses: " + (stop - start);

}

window.onload = function () {
    document.getElementById('b').onclick = t;
}

</script>
</head>
<body>
<div align="center"><button id="b">Run Test</button></div>
<div id="a"></div>
<div id="c"></div>
</body>

</html>

A counter-example, demonstrating faster access to local variables.

var t0 = new Date();
var i; 
for (i=0; i<10000000; i++); 
var t1 = new Date(); 
function count() { for (var i=0; i<10000000; i++); } 
var t2 = new Date(); 
count(); 
var t3 = new Date(); 
d = document; 
d.write('Without local variables = ', t1-t0, '<br />'); 
d.write('With local variables = ', t3-t2, '<br />');
Community
  • 1
  • 1
Saul
  • 17,973
  • 8
  • 64
  • 88
  • That may not be completely true since javascript has to recurse through the prototypical chain until it finds the variable you are using at the global level. If you read O'Reilys "Javascript Patterns" they always recommend storing your global var (If you HAVE TO HAVE global vars in the first place) into local variables. – Keith.Abramo Oct 04 '11 at 15:31
  • @Keith.Abramo - Is it mentioned in the [ECMA-262 specification](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf)? I haven't heard about prototypical chain recursion before. – Saul Oct 04 '11 at 15:38
  • @Saul http://stackoverflow.com/questions/2895407/how-expensive-are-variables-in-javascript, http://www.nczonline.net/blog/2009/02/10/javascript-variable-performance/, etc. Scope chain matters! (Whether or not it's a *noticeable* difference is a different issue ;) – Dave Newton Oct 04 '11 at 15:45
  • @DaveNewton - I could be wrong but my impression is that scope resolution is a browser-specific detail. In terms of execution context, there are two possible approaches - each stack frame contains all relevant references or dynamic resolution. The former obviously has better performance but the latter is superior in terms of memory conservation. – Saul Oct 04 '11 at 16:07
  • @Saul Of course it is, but you're still going to have to take a hit on initial lookup if it's a global, AFAICT. You edited. Yep, you could create a frame containing all possible variable lookups all the time, but IMO that'd be a larger performance hit in the general case--I'd be surprised if any engines did that. – Dave Newton Oct 04 '11 at 16:09
  • @DaveNewton - Hard to say, I am not familiar enough with JavaScript virtual machine implementations. However a comment from an article You referenced before notes: `After running the identifier-depth test with node.js 0.1.31 and –oprofile=true i’m impressed that v8 produced nearly constant times whereas my firefox total fails I think this scope-depth performance gap is just a matter of times until its fixed in all engines.`. So go figure - some say it is important while others say it is not. – Saul Oct 04 '11 at 16:23
  • @Saul But that means it's "important" unless you're locked in to a specific JS VM. Again, the key is if it's a non-trivial difference, like in an event loop, etc., and you need (relatively) consistent performance across VMs. – Dave Newton Oct 04 '11 at 16:27
  • @DaveNewton - Look, the question is about whether local scope access is faster than global scope access. In practice, access speeds appear to be equal (see update). – Saul Oct 04 '11 at 17:46
  • @Saul I never said anything different. (Although with a contrived benchmark like that it's hard to take the results seriously.) – Dave Newton Oct 04 '11 at 18:09
  • @DaveNewton - Visual aesthetics are hardly a criterion for a benchmark and I think its functionality is quite relevant - it compares global access from a nested scope to local access. You are welcome to improve on it or contribute a better alternative nevertheless. – Saul Oct 04 '11 at 18:25
  • @Saul Who said anything about visual aesthetics? Why are you angry? With an essentially empty symbol table, and empty functionality, you're providing a huge window of optimization opportunity. All I'm saying is, as anybody that's familiar with benchmarks realizes, an unrealistic benchmark isn't a benchmark. Geez--I'll let you continue this one on your own. – Dave Newton Oct 04 '11 at 18:25
  • @DaveNewton - Uh.. right. I was kind of expecting for a working counter-example. – Saul Oct 04 '11 at 19:07
  • @Saul Why? (Rhetorical. But I'm pretty sure I never implied I'd be providing any such thing.) Here's a fake benchmark anyway. var t0 = new Date(); var i; for (i=0; i<10000000; i++); var t1 = new Date(); function count() { for (var i=0; i<10000000; i++); } var t2 = new Date(); count(); var t3 = new Date(); d = document; d.write('Without local variables = ', t1-t0, '
    '); d.write('With local variables = ', t3-t2, '
    '); Order of magnitude difference on Linux, 14.0.835.186. But really, you're on your own now
    – Dave Newton Oct 04 '11 at 19:22
  • @DaveNewton - It is the OP and future visitors you are helping here, not me personally. That short benchmark is indeed a counter-example, I'll add it to the answer. – Saul Oct 05 '11 at 07:01
2

The reason for the performance difference you are talking about lies in the way javascript resolves the name of a variable to the value it refers to. So the (negligible) time lag is the result of javascript looking up the variable. When something is assigned by reference to something, it is in reference to the actual value in memory rather than the name.

For example, imagine you have a javascript variable called info with the value of { question: null, answer: 42 }. If you were to make the assignment

info2 = info;

You are telling javascript to find that value right now and use it when referring to info2. Now both info and info2 are pointing to the same value (the same memory address). You are not telling javascript to look up the reference info and get that value every time you use info2. This means that the way you are doing it is fine, since you are only looking up the global variable references once.

Note that javascript will only assign by reference for non-constants, so:

var a = 1;
var b = a;
var a = 2;
var b === 1; // => true

var z = { a: 1 };
var y = z;
z.a = 2;
y.a === 2; // => true

Primitive values are constants, objects are not.

benekastah
  • 5,651
  • 1
  • 35
  • 50