coffeescript比javascript更快?

时间:2012-01-28 17:26:53

标签: javascript performance html5 coffeescript benchmarking

Javascript无处不在,在我看来,它一直在变得越来越重要。大多数程序员都同意,虽然Javascript本身很难看,但它的“领域”确实令人印象深刻。凭借HTML5的功能和现代浏览器的速度,通过Javascript部署应用程序是一个有趣的选择:它可能是你可以获得的跨平台。

自然结果是交叉编译器。主要可能是GWT,但还有其他几种选择。我最喜欢的是Coffeescript,因为它只在Javascript上添加了一个薄层,并且比例如GWT更“轻量级”。

只有一件事让我烦恼:虽然我的项目相当小,但性能始终是一个重要的话题。这是一个引用

  

GWT SDK提供了一组核心Java API和小部件。这些允许   你用Java编写AJAX应用程序然后编译源代码   高度优化的JavaScript

Coffeescript也被优化了吗?由于Coffeescript似乎大量使用非常见的Javascript功能,我担心他们的性能如何比较。

您是否有过与Coffeescript相关的速度问题的经验? 你知道一个很好的基准比较吗?

5 个答案:

答案 0 :(得分:37)

为复活一个古老话题而道歉,但这也让我很担心。我决定执行一个小测试,我知道的一个最简单的性能测试是将连续值写入数组,随着数组的增长,内存以熟悉的方式消耗,并且'for'循环在现实生活中足够常见,需要考虑相关的。

经过几次红色鲱鱼后,我发现coffeescript最简单的方法是:

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

这使用闭包并返回数组作为结果。我的等价物是:

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

正如您所看到的结果是相同的,它也以类似的方式增长数组。接下来,我用chrome分析了每次100次的平均值。

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

Coffeescript慢了78%。我反驳说“你写的CoffeeScript最终运行的速度和你写的JS一样快(并且通常比你快)”(Jeremy Ashkenas


附录:我还怀疑人们普遍认为“JS中始终存在一对一的等同物”。我尝试用这个重新创建自己的代码:

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

尽管有相似之处,它仍然被证明减慢了7%,因为它增加了对方向(增量或减量)的额外检查,这意味着它不是直接翻译。

答案 1 :(得分:23)

这一切都非常有趣,而且有一个道理,咖啡脚本无法比完全优化的javascript更快地运行。

那就是说,因为咖啡脚本正在生成javascript。有办法让它值得。可悲的是,情况似乎并非如此。

让我们举个例子:

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

用咖啡脚本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);

clockworkgeek提供的代码是

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

但是由于咖啡脚本隐藏了范围内的函数,我们也应该为javascript执行此操作。我们不想对窗口进行正确的评估吗?

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

所以这里我们的代码实际上做了同样的事情。然后我们想实际测试两个版本。

咖啡脚本

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

生成JS,你可能会问自己那里发生了什么?无论出于何种原因,它都会创建i_i。从这两个方面我很清楚,只需要一个。

// 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);

所以现在我们要更新我们的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);

结果如下:

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

js需要

time node test2.js

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

所以你可能会问自己......什么地狱咖啡脚本更快?然后你看看代码并问自己......所以让我们试着解决它!

(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);

然后我们会对JS脚本做一个小修补,然后将a[i] = i更改为a.push(i)然后让我们再试一次......然后再加上BOOM

time node test2.js

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

这个小小的改动使它比我们的CoffeeScript Now更快,让我们看看生成的CoffeeScript ...并删除那些双重变量......

到此:

// 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);

和BOOM

time node test.js

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

我想说的是,使用更高级的语言有很多好处。生成的CoffeeScript未进行优化。但与纯粹的js代码相差不远。 clockworkgeek试图直接使用索引而不是推送使用的代码优化实际上似乎适得其反,并且比生成的coffeescript工作得慢。

事实上,这种优化很难找到并修复。另一方面,从版本到版本,coffeescript可以为当前的浏览器或解释器生成优化的js代码。 CoffeeScript将保持不变,但可以再次生成以加快速度。

如果你直接在javascript中编写,现在有办法真正优化代码,就像使用真正的编译器一样。

另一个有趣的部分是,有一天,CoffeeScript或javascript的其他生成器可用于分析代码(如jslint)并删除不需要某些变量的代码部分...使用不同的参数编译函数在不需要某些变量时加快速度。如果你有purejs,你将不得不期望有一个JIT编译器能够正确地完成工作并且它对coffeescript也有好处。

例如,我可以最后一次优化咖啡脚本..通过从for循环中删除new_way = (function...。一个聪明的程序员会知道,这里唯一发生的事情是对每个循环的函数的影响不会改变变量。该函数在函数范围内创建,不会在每个循环中重新创建。那说不应该改变太多......

time node test.js

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

所以这就是它。

答案 2 :(得分:11)

简短回答:

CoffeeScript生成javascript,因此其最大可能速度等于javascript的速度。但是你可以在低级优化js代码(是的,这听起来很讽刺)并获得一些性能提升 - 使用CoffeeScript你不能这样做。

但是在选择CS over JS时,代码的速度不应该是您的关注,因为大多数任务的差异可以忽略不计。

答案 3 :(得分:8)

Coffescript直接编译到JavaScript,这意味着在JS中对于任何Coffeescript源始终存在一对一的等价物。关于它没有什么不相同之处。性能提升可以来自优化的事物,例如事实上Coffescript将Array长度存储在for循环中的单独变量中,而不是在每次迭代中请求它。但这也应该是JavaScript中的常见做法,它本身并不是由语言本身强制执行的。

答案 4 :(得分:1)

我想在LoïcFaure-Lacroix的答案中添加一些内容......

看来,您只打印了一个浏览器的时间。根据jsperf,btw“x.push(i)”并不快“x [i] = i”:https://jsperf.com/array-direct-assignment-vs-push/130

Chrome:  push => 79,491  ops/s; direct assignment => 3,815,588 ops/s;
IE Edge: push => 358,036 ops/s; direct assignment => 7,047,523 ops/s;
Firefox: push => 67,123  ops/s; direct assignment => 206,444   ops/s;

另一点 - &gt; x.call(this)和x.apply(this)......我没有看到任何性能原因。甚至jsperf也证实了这一点:http://jsperf.com/call-apply-segu/18

Chrome: 
direct call => 47,579,486 ops/s; x.call => 45,239,029 ops/s; x.apply => 15,036,387 ops/s;
IE Edge: 
direct call => 113,210,261 ops/s; x.call => 17,771,762 ops/s; x.apply => 6,550,769 ops/s;
Firefox:
direct call => 780,255,612 ops/s; x.call => 76,210,019 ops/s; x.apply => 2,559,295 ops/s;

首先提一下 - 我使用了实际的浏览器。

其次 - 我通过for循环扩展了测试,因为只需一次调用,测试就会缩短...

最后但并非最不重要 - 现在所有浏览器的测试如下:

在这里,我使用了CoffeeScript 1.10.0(使用他的答案中给出的相同代码编译)

console.time('coffee');// added manually
(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);
  };

  // manually added on both
  var i;
  for(i = 0; i != 10; i++)
  {
    new_way();
  }

}).call(this);
console.timeEnd('coffee');// added manually

现在是Javascript

console.time('js');
(function() {

  function old_way()
  {
    var i = 0, results = [];
    return (function()
    {
      for (i = 0; i <= 1000000; i++)
      {
        results[i] = i;
      }
      return results;
    })();// replaced apply
  }

  var i;
  for(i = 0; i != 10; i++)
  {
    old_way();
  }

})();// replaced call
console.timeEnd('js');

for循环的限制值很低,因为任何更高的测试都是非常缓慢的测试(10 * 1000000次调用)......

结果

Chrome:  coffee: 305.000ms;   js: 258.000ms;
IE Edge: coffee: 5.944,281ms; js: 3.517,72ms;
Firefox: coffee: 174.23ms;    js: 159.55ms;

在这里我必须要提到的是,咖啡在这次测试中并不总是最慢的。你可以通过在firefox中测试这些代码来看到它。

我的最终答案:

首先要说的是 - 我对coffeescript并不是很熟悉,但是我调查了它,因为我正在使用Atom编辑器并想尝试在那里构建我的第一个包,但是又回到了Javascript ... 所以,如果有任何错误,你可以纠正我。

使用coffeescript,您可以编写更少的代码,但如果涉及优化,则代码会变得很重。我自己的意见 - &gt;我没有在这种Coffeescripting语言中看到任何所谓的“生产力”......

回到表演::最常用的浏览器是Chrome浏览器(src:w3schools.com/browsers/browsers_stats.asp),占60%,我的测试也显示手动输入的Javascript运行速度比Coffeescript(IE除外...... - 快得多)。我会推荐Coffeescript用于较小的项目,但如果没有人介意,请保持你喜欢的语言。