Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is coffeescript faster than javascript?

Javascript is everywhere and to my mind is constantly gaining importance. Most programmers would agree that while Javascript itself is ugly, its "territory" sure is impressive. With the capabilities of HTML5 and the speed of modern browsers deploying an application via Javascript is an interesting option: It's probably as cross-platform as you can get.

The natural result are cross compilers. The predominant is probably GWT but there are several other options out there. My favourite is Coffeescript since it adds only a thin layer over Javascript and is much more "lightweight" than for example GWT.

There's just one thing that has been bugging me: Although my project is rather small performance has always been an important topic. Here's a quote

The GWT SDK provides a set of core Java APIs and Widgets. These allow you to write AJAX applications in Java and then compile the source to highly optimized JavaScript

Is Coffeescript optimized, too? Since Coffeescript seems to make heavy use of non-common Javascript functionality I'm worried how their performance compares.

Have you experience with Coffeescript related speed issues ? Do you know a good benchmark comparison ?

like image 371
lhk Avatar asked Jan 28 '12 17:01

lhk


People also ask

Why should you use CoffeeScript instead of JavaScript?

CoffeeScript is something that makes even good JavaScript code better. CoffeeScript compiled code can do everything that natively written JavaScript code can, only the code produced by using CoffeeScript is way shorter, and much easier to read.

Is CoffeeScript obsolete?

In summary, CoffeeScript began as a fantastic idea (making it easier to write JavaScript code); ultimately, however, it didn't stand the test of time and was pushed out by JavaScript. Currently, hardly anyone remembers it.

Which is faster JavaScript or C++?

C++ is more faster as compared to JavaScript. JavaScript is little slower as compared to C++ programming language.


2 Answers

Apologies for resurrecting an old topic but it was concerning me too. I decided to perform a little test and one of the simplest performance tests I know is to write consecutive values to an array, memory is consumed in a familiar manner as the array grows and 'for' loops are common enough in real life to be considered relevant.

After a couple of red herrings I find coffeescript's simplest method is:

newway = -> [0..1000000]
# simpler and quicker than the example from http://coffeescript.org/#loops
# countdown = (num for num in [10..1])

This uses a closure and returns the array as the result. My equivalent is this:

function oldway()
{
    var a = [];
    for (var i = 0; i <= 1000000; i++)
        a[i] = i;
    return a;
}

As you can see the result is the same and it grows an array in a similar way too. Next I profiled in chrome 100 times each and averaged.

newway() | 78.5ms
oldway() | 49.9ms

Coffeescript is 78% slower. I refute that "the CoffeeScript you write ends up running as fast as (and often faster than) the JS you would have written" (Jeremy Ashkenas)


Addendum: I was also suspicious of the popular belief that "there is always a one to one equivalent in JS". I tried to recreate my own code with this:

badway = ->
    a = []
    for i in [1..1000000]
        a[i] = i
    return a

Despite the similarity it still proved 7% slower because it adds extra checks for direction (increment or decrement) which means it is not a straight translation.

like image 191
clockworkgeek Avatar answered Nov 15 '22 19:11

clockworkgeek


This is all quite intersting and there is one truth, coffee script cannot work faster than fully optimized javascript.

That said, since coffee script is generating javascript. There are ways to make it worth it. Sadly, it doesn't seem to be the case yet.

Lets take the example:

new_way = -> [0..1000000]
new_way()

It compiles to this with coffee script 1.6.2

// Generated by CoffeeScript 1.6.2
(function() {
  var new_way;

  new_way = function() {
    var _i, _results;

    return (function() {
      _results = [];
      for (_i = 0; _i <= 1000000; _i++){ _results.push(_i); }
      return _results;
    }).apply(this);
  };

  new_way();

}).call(this);

And the code provided by clockworkgeek is

function oldway()
{
    var a = [];
    for (var i = 0; i <= 1000000; i++)
        a[i] = i;
    return a;
}
oldway()

But since the coffee script hides the function inside a scope, we should do that for javascript too. We don't want to polute window right?

(function() {
    function oldway()
    {
        var a = [];
        for (var i = 0; i <= 1000000; i++)
            a[i] = i;
        return a;
    }
    oldway()
}).call(this);

So here we have code that does the same thing actually. And then we'd like to actually test both versions a couple of time.

Coffee script

for i in [0..100]
    new_way = -> [0..1000000]
    new_way()

Generated JS, and you may ask yourself what is going on there??? It's creating i and _i for whatever reason. It's clear to me from these two, only one is needed.

// Generated by CoffeeScript 1.6.2
(function() {
  var i, new_way, _i;

  for (i = _i = 0; _i <= 100; i = ++_i) {
    new_way = function() {
      var _j, _results;

      return (function() {
        _results = [];
        for (_j = 0; _j <= 1000000; _j++){ _results.push(_j); }
        return _results;
      }).apply(this);
    };
    new_way();
  }

}).call(this);

So now we're going to update our Javascript.

(function() {
    function oldway()
    {
        var a = [];
        for (var i = 0; i <= 1000000; i++)
            a[i] = i;
        return a;
    }

    var _i;

    for(_i=0; _i <= 100; ++_i) {
        oldway()
    }
}).call(this);

So the results:

time coffee test.coffee

real    0m5.647s
user    0m0.016s
sys     0m0.076s

time node test.js

real    0m5.479s
user    0m0.000s
sys     0m0.000s

The js takes

time node test2.js

real    0m5.904s
user    0m0.000s
sys     0m0.000s

So you might ask yourself... what the hell coffee script is faster??? and then you look at the code and ask yourself... so let's try to fix that!

(function() {
    function oldway()
    {
        var a = [];
        for (var i = 0; i <= 1000000; i++)
            a.push(i);
        return a;
    }

    var _i;

    for(_i=0; _i <= 100; ++_i) {
        oldway()
    }
}).call(this);

We'll then do a small fix to the JS script and change a[i] = i to a.push(i) And then lets try again...and then BOOM

time node test2.js

real    0m5.330s
user    0m0.000s
sys     0m0.000s

This small change made it faster than our CoffeeScript Now lets look at the generated CoffeeScript... and remove those double variables...

to this:

// Generated by CoffeeScript 1.6.2
(function() {
  var i, new_way;

  for (i = 0; i <= 100; ++i) {
    new_way = function() {
      var _j, _results;

      return (function() {
        _results = [];
        for (_j = 0; _j <= 1000000; _j++){ _results.push(_j); }
        return _results;
      }).apply(this);
    };
    new_way();
  }

}).call(this);

and BOOM

time node test.js

real    0m5.373s
user    0m0.000s
sys     0m0.000s

Well what I'm trying to say is that there are great benefits to use a higher language. The generated CoffeeScript wasn't optimized. But wasn't that far from the pure js code. The code optimization that clockworkgeek tried to use with using index directly instead of push actually seemed to backfire and worked slowlier than the generated coffeescript.

The truth it that such kind of optimization could be hard to find and fix. On the other side, from version to version, coffeescript could generate optimized js code for current browser or interpreters. The CoffeeScript would remain unchanged but could be generated again to speedup things.

If you write directly in javascript, there is now way to really optimize the code as much as one would with a real compiler.

The other interesting part is that one day, CoffeeScript or other generators to javascript could be used to analyse code (like jslint) and remove parts of the code where some variables aren't needed... Compile functions differently with different arguments to speed up things when some variables aren't needed. If you have purejs, you'll have to expect that there is a JIT compiler that will do the job right and its good for coffeescript too.

For example, I could optimize the coffee script one last time..by removing the new_way = (function... from inside the for loop. One smart programmer would know that the only thing that happen here is reaffection the function on each loop which doesn't change the variable. The function is created in the function scope and isn't recreated on each loop. That said it shouldn't change much...

time node test.js

real    0m5.363s
user    0m0.015s
sys     0m0.000s

So this is pretty much it.

like image 28
Loïc Faure-Lacroix Avatar answered Nov 15 '22 20:11

Loïc Faure-Lacroix