JavaScript speed test

No one should care about JavaScript performance. But if you do, this page will help you get a feel for which operations are fast and which are slow. For example, you might be surprised to find that accessing array elements is no faster than accessing object properties.

How to play. Double-click any box in the “Results” column to run the test. Or click the “Start tests” button to run them all; but that takes a while. Each test takes 2 or 3 seconds to run.

Details. How does the tester work? Why is the code so convoluted? Can I depend on the results for my nuclear or medical application? All is revealed below.

Internet Explorer troubleshooting. In IE, some tests trigger the infamous “A script on this page is making Internet Explorer run slowly” popup. This makes the test results useless. Fortunately there's a workaround, discovered by Tony Mills. Just tweak the MaxScriptStatements registry setting and IE will totally chill. Tony writes: “The KB entry has an error though. The key is actually ‘Internet Explorer’ not ‘InternetExplorer’. ...By the way, further testing revealed that setting the limit to 0 doesn't remove the message, it just makes IE really annoying. :) So 0xFFFFFFFF is the best value to use.”

Test Code Result
Empty loop (none)

Variables

These tests compare the performance of local, global, and closure variables.

Create local variable
var x;
Create 10 local variables
var x1, x2, x3, x4, x5, x6, x7, x8, x9, x10;
Create and initialize local variable
var x = 13;
Increment local
var i = 0;
%%
i++;
Access local
var _test_value = 123;
%%
_test_value;
Access global
window._test_value = 123;
%%
_test_value;
Access variable in closure
function makeFn(n) {
	return function() {
		var nTests = _n;
		for (var i = 0; i < nTests; i++)
			n;
	}
};
var runTest = makeFn(123);
%%
runTest();
break;
Assign local
var _test_value;
%%
_test_value = 123;
Assign global
window._test_value = 123;
%%
_test_value = 123;
Assign to variable in closure
function makeFn(n) {
	return function() {
		var nTests = _n;
		for (var i = 0; i < nTests; i++)
			n = 123;
	}
};
var runTest = makeFn(123);
%%
runTest();
break;

Calling functions

On my machine, function calls are several times faster in Mozilla than in IE.

Calling a global function is slower than calling a local function, but only because of the name lookup.

In IE, the last test here consistently runs about 5% faster than the very similar one immediately preceding it. No idea why.

Call locally defined function
function f() {}
%%
f();
Call locally created anonymous function
var f1 = function () {};
%%
f1();
Call global function
window.f1 = new Function("");
%%
f1();
Call function created with new Function
var f5 = new Function ("");
%%
f5();
Call global function via local alias
window.f6 = new Function("");
var f6local = f6;
%%
f6local();

Creating functions

In Mozilla, a function expression without a name runs twice as fast as one with a name.

In IE, defining a local function takes no time at all. The local function is created as soon as the function is called, and it is not re-created each time the test loop runs. So the test loop is essentially empty. In Mozilla, a new function object is created each time through the loop.

A call to new Function is slower than a function expression, presumably because it has to compile the code.

Define local function
function f() {}
Create function using function expression without name
var f;
%%
f = function () {};
Create function using function expression with name
var f;
%%
f = function jane() {};
Create function using Function constructor
var f;
%%
f = new Function("");
Create nonempty function
var f;
%%
f = function elementText(elt) {
		var a = [];
		for (var n = elt.firstChild;
			n !== null;
			n = n.nextSibling)
		{
			if (n.nodeType == 3 || n.nodeType == 4)
				a.push(n.nodeValue);
		}
		return a.join("");
	};
Create deeply nested function
var a = ["var myfn = function () { "];
for (var i = 0; i < 500; i++)
	a.push("return function () { ");
a.push("return null;");
for (var i = 0; i < 500; i++)
	a.push(" };");
a.push("}; myfn");
var makeFn = eval(a.join(""));
var f;
%%
f = makeFn();

Arrays

Create empty array
([]);
Create small array
([1, 2, 3]);
Create small array and local variable
var a = [1, 2, 3];
Access elements by index
var a = [1, 2, 3, 4, 5];
%%
a[3]
Populate 10-element array using element assignment
var a;
%%
a = [];
for (var i = 0; i < 10; i++)
	a[i] = i;
Populate 10-element array using push()
var a;
%%
a = [];
for (var i = 0; i < 10; i++)
	a.push(i);
Populate 1000-element array of numbers using element assignment
var a;
%%
a = [];
for (var i = 0; i < 1000; i++)
	a[i] = i;
Populate 1000-element array of numbers using push()
var a;
%%
a = [];
for (var i = 0; i < 1000; i++)
	a.push(i);
Populate 1000-element array of objects using push()
var a;
var x = {};
%%
a = [];
for (var i = 0; i < 1000; i++)
	a.push(x);
Pop
var arrays = [];
for (var i = 0; i < _n; i++) {
	var a = [];
	for (j = 0; j < 1000; j++)
		a[j] = j;
	arrays[i] = a;
}
%%
var a = arrays[_i];
for (var i = 0; i < 1000; i++)
	a.pop();

Maintaining the length of an array in a separate variable can make stack operations several times faster.

1000 push() and pop() calls interleaved
var a = [];
%%
for (var i = 0; i < 100; i++) {
	a.push(17);
	a.push(19);
	a.push(31);
	a.pop();
	a.push(83);
	a.push(7);
	a.pop();
	a.pop();
	a.pop();
	a.pop();
}
1000 a[len++]= and a[--len] operations interleaved
var a = [];
var len = 0;
%%
for (var i = 0; i < 100; i++) {
	a[len++] = 17;
	a[len++] = 19;
	a[len++] = 31;
	a[--len];
	a[len++] = 83;
	a[len++] = 7;
	a[--len];
	a[--len];
	a[--len];
	a[--len];
}

Objects

Create empty object
({});
Create empty object and local variable
var x = {};
Populate 1000 numbered properties
var x;
%%
x = {};
for (var i = 0; i < 1000; i++)
	x[i] = i;
Populate 1000 numbered properties with object
var x;
var y = {};
%%
x = {};
for (var i = 0; i < 1000; i++)
	x[i] = y;

Object type-checking

Object.isPrototypeOf() is slow: 2-3 times slower than a simple attribute test.

Object.isPrototypeOf() when true
function writer() {}
var x = new writer();
%%
if (writer.prototype.isPrototypeOf(x)) {
	// ...
} else {
	// ...
}
Attribute test when true
function writer() {}
writer.prototype.isWriter = true;
var x = new writer();
%%
if (x.isWriter) {
	// ...
} else {
	// ...
}
Object.isPrototypeOf() when trivially false
function writer() {}
var x = {};
%%
if (writer.prototype.isPrototypeOf(x)) {
	// ...
} else {
	// ...
}
Attribute test when trivially false
var x = {
	make: 'Ford', model: 'Taurus',
	year: 1996, color: '#000066'};
%%
if (x.isWriter) {
	// ...
} else {
	// ...
}
Object.isPrototypeOf() when elaborately false
function writer() {}
function reader() {}
function http_reader() {}
http_reader.prototype = new reader();
http_reader.prototype.constructor = http_reader;
var x = new http_reader();
%%
if (writer.prototype.isPrototypeOf(x)) {
	// ...
} else {
	// ...
}
Attribute test when elaborately false
function writer() {}
writer.prototype.isWriter = true;

function reader() {}
function http_reader() {}
http_reader.prototype = new reader();
http_reader.prototype.constructor = http_reader;
var x = new http_reader();
%%
if (x.isWriter) {
	// ...
} else {
	// ...
}

Data structures

What's the fastest possible way to build and access data structures in JavaScript?

Objects and arrays are about equally fast. Using closures to store data (a trick from functional programming) is slower.

Create 2-property object
var nil = {};
var cell;
%%
cell = {car: nil, cdr: nil};
Create 2-property object using new
var nil = {};
function pair(a, b) {
	this.car = a;
	this.cdr = b;
}
var cell;
%%
cell = new pair(nil, nil);
Create 2-element array
var nil = {};
var cell;
%%
cell = [nil, nil];
Create closure on 2 values
var nil = {};
var cell;
var a = nil;
var b = nil;
%%
cell = function (t) { return t ? a : b; };
Access object properties
var nil = {};
var cell = {car: nil, cdr: nil};
%%
cell.cdr;
Access array elements
var nil = {};
var cell = [nil, nil];
%%
cell[1];
Access data hidden in closure
function cons(a, b) {
	return function(t) {
		return t ? a : b;
	}
}
var nil = {};
var cell = cons(nil, nil);
%%
cell(false);
Assign object properties
var nil = {};
var cell = {car: nil, cdr: nil};
%%
cell.cdr = 123;
Assign array elements
var nil = {};
var cell = [nil, nil];
%%
cell[1] = 123;

if and switch

On my machine, a switch statement with five cases is twice as fast as an if statement with five branches.

if comparing small integers, hitting the first
var x = 0;
%%
if (x == 0) {
	// ...
} else if (x == 1) {
	// ...
} else if (x == 2) {
	// ...
} else if (x == 3) {
	// ...
} else if (x == 4) {
	// ...
}
switch on small integers, hitting the first
var x = 0;
%%
switch (x) {
	case 0: break;
	case 1: break;
	case 2: break;
	case 3: break;
	case 4: break;
}
if comparing small integers, hitting the last
var x = 4;
%%
if (x == 0) {
	// ...
} else if (x == 1) {
	// ...
} else if (x == 2) {
	// ...
} else if (x == 3) {
	// ...
} else if (x == 4) {
	// ...
}
switch on small integers, hitting the last
var x = 4;
switch (x) {
	case 0: break;
	case 1: break;
	case 2: break;
	case 3: break;
	case 4: break;
}
if missing everything
var x = 4;
%%
if (x == 61) {
	// ...
} else if (x == 312) {
	// ...
} else if (x == 928) {
	// ...
} else if (x == 1003) {
	// ...
} else if (x == 5778) {
	// ...
}
switch missing everything
var x = 4;
%%
switch (x) {
	case 61:   break;
	case 312:  break;
	case 928:  break;
	case 1003: break;
	case 5778: break;
}
if comparing characters, missing everything
var c = 'e';
%%
if (c == '\n') {
	// ...
} else if (c == 'y') {
	// ...
} else if (c == 'n') {
	// ...
} else if (c == 'q') {
	// ...
} else if (c == 'a') {
	// ...
}
switch on characters, missing everything
var c = 'e';
%%
switch (c) {
	case '\n': break;
	case 'y':  break;
	case 'n':  break;
	case 'q':  break;
	case 'a':  break;
}

Dynamic dispatch

What's the fastest way to execute different code depending on the value of a string variable? switch is the fastest.

if
var x = false;
var s = 'five';
%%
if (s == 'one') { x=true; }
else if (s == 'two') { x=true; }
else if (s == 'three') { x=true; }
else if (s == 'four') { x=true; }
else if (s == 'five') { x=true; }
else if (s == 'six') { x=true; }
else if (s == 'seven') { x=true; }
else if (s == 'eight') { x=true; }
else if (s == 'nine') { x=true; }
else if (s == 'ten') { x=true; }
switch
var x = false;
var s = 'five';
%%
switch (s) {
case 'one': x=true; break;
case 'two': x=true; break;
case 'three': x=true; break;
case 'four': x=true; break;
case 'five': x=true; break;
case 'six': x=true; break;
case 'seven': x=true; break;
case 'eight': x=true; break;
case 'nine': x=true; break;
case 'ten': x=true; break;
}
lookup table of functions
var x = false;
var obj = {
	one: function () { x=true; },
	two: function () { x=true; },
	three: function () { x=true; },
	four: function () { x=true; },
	five: function () { x=true; },
	six: function () { x=true; },
	seven: function () { x=true; },
	eight: function () { x=true; },
	nine: function () { x=true; },
	ten: function () { x=true; }
};
var s = 'five';
%%
obj[s]();

Loops

There's no speed difference between ++c and c++ in a for loop.

All these should be equally fast. In Mozilla, the for loop is just a hair faster than while; no clue why. And do/while is 5-10% faster still; I have no idea what that's all about.

The do/while test triggers IE's “run slowly” message on my machine; the others don't. Bizarre.

for up to 100
var c;
%%
for (c = 0; c < 100; c++) {
}
for up to 100, preincrement
var c;
%%
for (c = 0; c < 100; ++c) {
}
while up to 100
var c;
%%
c = 0;
while (c < 100) {
	c++;
}
do/while up to 100
var c;
%%
c = 0;
do {
	c++;
} while (c < 100);

Whitespace

Whitespace in source code doesn't affect speed.

(Whitespace affects running code in at least one way: when an error happens, the system tries to tell you what line of code it happened on. Some scripting languages used to implement this using an internal line-number counter, which was incremented at the beginning of every line of code. The line-number counting could actually affect performance in a tight loop. I don't know of any major languages that still do this.)

on one line
var i = 0;
%%
i = Number(i); i++; i++; i++; i++; i++; i++; i++; i++; i++; i++;
on multiple lines
var i = 0;
%%
i = Number(i);
i++;
i++;
i++;
i++;
i++;
i++;
i++;
i++;
i++;
i++;

Text processing

What is the fastest way to tell whether a given character c is one of a particular known set of characters?

Interesting results. Almost all of these are dog slow in Mozilla. Except the last one.

In IE, the last option is three times faster than the rest. In Mozilla, it's ten times faster.

Character testing: longhand
var c = '(';
var hits = 0;
%%
if (c.length == 1 && (
	(c >= 'a' && c <= 'z')
	|| (c >= 'A' && c <= 'Z')
	|| (c >= '0' && c <= '9')
	|| c == '-')) {}
Character testing: by character code
var c = '(';
var hits = 0;
%%
var i = c.charCodeAt(0);
if ((i >= 97 && i <= 122)
	|| (i >= 65 && i <= 90)
	|| (i >= 48 && i <= 57)
	|| i == 45) {}
Character testing: with String.indexOf()
var w = ("abcdefghijklmnopqrstuvwxyz"
	+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-");
var c = '(';
%%
if (w.indexOf(c) != -1) {}
Character testing: with regular expression
var wx = /[a-zA-Z0-9-]/;
var c = '(';
var hits = 0;
%%
if (c.match(wx) !== null) {}
Character testing: by hashtable
var c = '(';
var w = ("abcdefghijklmnopqrstuvwxyz"
	+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-");
var whash = {};
for (var i = 0; i < w.length; i++)
	whash[w.charAt(i)] = true;
var hits = 0;
%%
if (whash[c]) {}

For more complex parsing, regular expressions are 20-50 times faster than a hand-coded parse routine.

Parsing: with sloppy regular expression
var text = document.documentElement.innerHTML;
var regex = /<([\w-]+)/g;
var matchArray;
var match;
var count = 0;
%%
while ((matchArray = regex.exec(text)) !== null) {
	match = matchArray[1];
	count++;
}
Parsing: with sloppy hand-coded loop
var text = document.documentElement.innerHTML;
var i;
var match;
var count = 0;
var tagNameChars = (
	'abcdefghijklmnopqrstuvwxyz'
	+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-');
%%
i = 0;
while (i < text.length) {
	// search for angle bracket
	while (i < text.length && text.charAt(i) != '<')
		i++;
	if (++i >= text.length)
		break;

	var c = text.charAt(i);
	if (tagNameChars.indexOf(c) == -1)
		continue;

	var b = i;
	while (++i < text.length
		&& tagNameChars.indexOf(text.charAt(i)) != -1)
		;
	match = text.substring(b, i);
	count++;
}

Details

Overhead. All results include the overhead of a simple for loop; run the “Empty loop” test to see how much this is.

Accuracy. Forget it. The results are very noisy. Double-click any cell two or three times and you'll get different results. I imagine you can get maybe one significant digit out of these, maybe a bit less.

How it works. For each test, the tester uses new Function() to create a function that takes a positive whole number N and times the execution of the test code N times in a tight loop. Then it calls the function repeatedly, with N=1, then 2, then 5, 10, 20, 50, and so on until the loop actually takes a significant amount of time to execute (at least 200ms). It does this 5 times, throws out the worst time, and averages the other four. This average is what's displayed.

The tester code is written in continuation-passing style, an extremely weird way to program that helps to avoid triggering the infamous “A script on this page is making Internet Explorer run slowly” popup. Alas, this message pops up on the fastest tests regardless, unless you use the workaround described at the top of the page.