Andrey Hihlovskiy

Professional blog on groovy, gradle, Java, Javascript and other stuff.

Critique on “4 more Javascript hacks to make your javascript faster”

I generally praise Nicholas Ortenzio in CSS Perverts for writing an article “4 more Javascript hacks to make your JavaScript faster”. That could be a good article, if only author would not make so many mistakes in his statements and conclusions.

update-20140113:
OK, people, thank you so much for explaining me it was a “satire”. Call it trolling, whatever. I will not sermonize or edify anyone. I truly regret my precious time I spent reading this bullsh*t and analyzing it. If it was the goal of the article’s author, he/she completely reached his/her goal.

The left of my response I leave unchanged.

Let’s parse an article step by step:

1. Get rid of those nested loops

Nested loops have a computational complexity of O(n^2). This means that if you have items, it will take n*n seconds to complete the loop.

Critique: This is true only for a special case: when nested-loop is applied to the same collection or to multidimensional array of uniform dimensions. There are many cases not falling into this category.

// because this loop is nested, it can
// take up to 100 seconds to complete
for (var i=0; i<10; i+=1) {
 for (var j=0; j<10; j+=1) {
 console.log(i, j);
 }
}
// better
for (var i=0, j=0; i<10 && j<10; j++, i=(j==10)?i+1:i,j=(j==10)?j=0:j) {
 console.log(i, j);
}
//best
for (var i=0, j=0; i<10 && j<10; j++, i=(j==10)?i+1:i,j=(j==10)?j=0:j, console.log(i, j)) { }

Critique: the statement in the first comment is plainly wrong. The loop takes around 10 milliseconds in Firefox on average computer. The slowest part is, of course, console.log (not the loop mechanism itself). As soon as console.log is replaced to, say, arithmetical operation, the loop performs under 1 millisecond. Since testing at 1 or 10 milliseconds does not give any reliable results, the test should be improved by increasing count to, say, 100 or 1000. At such counts the results are as follows:

// this loop finishes on average in 860 milliseconds
for (var i=0; i<100; i+=1) {
 for (var j=0; j<100; j+=1) {
   console.log(i, j);
 }
}
// this loop finishes on average in 940 milliseconds
for (var i=0, j=0; i<100 && j<100; j++, i=(j==100)?i+1:i,j=(j==100)?j=0:j) {
 console.log(i, j)
}
// this loop finishes on average in 960 milliseconds
for (var i=0, j=0; i<100 && j<100; j++, i=(j==100)?i+1:i,j=(j==100)?j=0:j, console.log(i, j) ) { }

Conclusion: results are actually getting worse, when the one tries to “optimize” loops that way.

To make results more reliable, lets replace calls to console.log by arithmetic and increase loop count to 1000:

// this loop finishes on average in 680 milliseconds
var z
for (var i=0; i<1000; i+=1) {
 for (var j=0; j<1000; j+=1) {
   z = i * j
 }
}
// this loop finishes on average in 1540 milliseconds
var z
for (var i=0, j=0; i<1000 && j<1000; j++, i=(j==1000)?i+1:i,j=(j==1000)?j=0:j) {
 z = i * j
}
// this loop finishes on average in 1560 milliseconds
var z
for (var i=0, j=0; i<1000 && j<1000; j++, i=(j==1000)?i+1:i,j=(j==1000)?j=0:j, z = i * j) { }

Again, we see – attempts to “optimize” a javascript loop only make things worse!

2. Bit shift instead of multiply

Did you know that at a molecular level, computers can only add (and they can just barely do that) Computers “multiply” by using a combination of expensive logarithmic look up tables and extremely lucky guesses. Since CPU’s are so fast, they can make dozens of guesses per second until they come up with the right answer.

Instead of multiplying, use the bit shift operation. it looks a little more complex, but once you get the hang of it, it’s pretty simple. The formula to multiply x * y is simply x << (y-1)

// multiply by 1
[1,2,3,4].forEach(function(n){ return n<<0; }) // 1,2,3,4
// multiply by 2
[1,2,3,4].forEach(function(n){ return n<<1; }) // 2,4,6,8
// multiply by 3
[1,2,3,4].forEach(function(n){ return n<<2; }) // 3,6,9,12
// etc

Regarding the “molecular level”, see “Instruction Latencies in Assembly Code for 64-Bit Intel® Architecture”   and the discussion on stackoverflow. Brief summary: modern CPUs implement floating-point multiplication as a single instruction, which is not slow at all.

“Instead of multiplying, use the bit shift operation” – wrong!

First mistake the author made is that he did not even test his own code – in its current form it does nothing. “forEach” function does not change an array, it simply calls the specified function for each member of the array. To make it work, the one needs to replace “forEach” with “map”.
And even when we fix and run the code, it returns wrong results:

console.log(JSON.stringify([1,2,3,4].map(function(n){ return n<<2; }))) // 3,6,9,12
// actually it prints [4,8,12,16] !

3. Round real numbers before multiplying them

The trick above is a great time save for integers. But it doesn’t work for so-called “floating point” numbers (FP for short). In this case, computers really SUPER slow. A Multiplication operation (known as a MUX in programming terms) using the two values, 9.52434123 takes forever (in milliseconds) to compute. You can’t always avoid FP MUX OPs, but there is a trick to speed them up if you can sacrifice a bit of precision. Simply round the number using the toFixed operation.

// pretty  much the same thing but the second line
// is much faster
9.52434123 * 2
9.52434123.toFixed(2) * 2

Wrong! When the abovementioned code is benchmarked, second variant is actually slower:

var x = 9.52434123;
var y = 2;
// this loop finishes on average in 7800 milliseconds
for(var i = 0; i < 10000000; i++)
  z = x * y;
var x = 9.52434123;
var y = 2;
// this loop finishes on average in 14100 milliseconds
for(var i = 0; i < 10000000; i++)
  z = x.toFixed(2) * y;

Again, modern computers are not super-slow on floating-point operations. Before making such statement, the one needs to read at least some literature and to do at least some benchmarking.

4 responses to “Critique on “4 more Javascript hacks to make your javascript faster”

  1. Sylvain Pollet (@SylvainPV) January 13, 2014 at 01:30

    So you did not realize the whole thing is a joke ?

  2. mornfall January 13, 2014 at 02:34

    Oh god. You do realize that the original article is satire? Please? It is completely and obviously full of nonsesnse.

  3. Kim Taegon (@taggon) January 13, 2014 at 08:48

    I totally agree with you. The author who wrote ‘4 more javascript hacks… whatever’ seems not to know any javascript and even CS 101.

Leave a reply to Sylvain Pollet (@SylvainPV) Cancel reply