使用Node,为什么在加上"use strict"后代码会更快?

19
我以前不知道use strict可以加速运行时间,但是一个简单的use strict让我的基准测试速度大大提高,而较慢的则明显变得更慢(慢了两倍以上)。发生了什么?
//
// RUN WITH AND WITHOUT THIS
//
"use strict";

var assert = require('assert');

var slice = [].slice;

function thunkify_fast(fn){
  assert('function' == typeof fn, 'function required');

  return function(){
    var args = new Array(arguments.length);
    for(var i = 0; i < args.length; ++i) {
      args[i] = arguments[i];
    }
    var ctx = this;

    return function(done){
      var called;

      args.push(function(){
        if (called) return;
        called = true;
        done.apply(null, arguments);
      });

      try {
        fn.apply(ctx, args);
      } catch (err) {
        done(err);
      }
    }
  }
};

function thunkify_slow(fn){
  assert('function' == typeof fn, 'function required');

  return function(){
    var args = slice.call(arguments);
    var ctx = this;

    return function(done){
      var called;

      args.push(function(){
        if (called) return;
        called = true;
        done.apply(null, arguments);
      });

      try {
        fn.apply(ctx, args);
      } catch (err) {
        done(err);
      }
    }
  }
};


var fn = function () { };

var Benchmark = require('benchmark');
var suite = new Benchmark.Suite;


//
// Only one wrapper can be sent through the optimized compiler
//
suite.add( 'thunkify#fast', function () { thunkify_fast(fn)(function(){}) } )
    .add( 'thunkify#slow', function () { thunkify_slow(fn)(function(){}) } )
    .on('cycle', function(event) { console.log(String(event.target)); })
    .on('complete', function() {
        console.log('Fastest is ' + this.filter('fastest').pluck('name'));
    })
    .run();

没有顶部的"use strict",结果与此相同。
$ node --allow-natives-syntax test.js 
thunkify#fast x 8,511,605 ops/sec ±1.22% (95 runs sampled)
thunkify#slow x 4,579,633 ops/sec ±0.68% (96 runs sampled)
Fastest is thunkify#fast

然而,使用"use strict;"后,我得到了以下结果:

$ node --allow-natives-syntax test.js 
thunkify#fast x 9,372,375 ops/sec ±0.45% (100 runs sampled)
thunkify#slow x 1,483,664 ops/sec ±0.93% (96 runs sampled)
Fastest is thunkify#fast

我正在运行nodejs v0.11.13。这是我正在做的工作的一部分,目的是使用这个指南加速node-thunkify。有趣的是,bluebird优化指南没有提到use strict;对性能的有益影响。

如果我更改测试用例为,

var f_fast = thunkify_fast(fn);
var f_slow = thunkify_slow(fn);
suite.add( 'thunkify#fast', function () { f_fast(function(){}) } )
  .add( 'thunkify#slow', function () { f_slow(function(){}) } )
  .on('cycle', function(event) { console.log(String(event.target)); })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').pluck('name'));
  })
  .run();

因此,移除了调用 thunkify,我仍然看到相同的东西。在未经优化的代码中使用严格模式会更慢,在经过优化的代码中会更快。

没有严格模式

thunkify#fast x 18,910,556 ops/sec ±0.61% (100 runs sampled)
thunkify#slow x 5,148,036 ops/sec ±0.40% (100 runs sampled)

"使用严格模式;"

thunkify#fast x 19,485,652 ops/sec ±1.27% (99 runs sampled)
thunkify#slow x 1,608,235 ops/sec ±3.37% (93 runs sampled)

你看过这个吗?特别是“use strict”如何执行以下操作:“它禁用了那些令人困惑或思考不周的功能。” https://dev59.com/ynM_5IYBdhLWcg3wiDuA - Giacomo1968
3
为什么禁用这些功能会使得未优化的函数运行时变得非常 缓慢,但对于优化后的函数,速度会提高超过10%? - Evan Carroll
1
我能得出的唯一结论是,严格模式必须以某种方式减慢对arguments的修改,并加速简单的迭代和元素赋值... - Evan Carroll
3个回答

14
这种缓慢的原因在于ArraySlice内置函数中的this check。它测试我们是否试图切片参数对象,如果是,则使用快速代码来执行操作。但是它仅检查松散模式参数对象。当您在严格函数内分配参数对象时,您会得到由native_context()->strict_arguments_boilerplate()创建的严格模式参数对象,这意味着上述检查无法识别它,并落入比专门手写的C++快速路径更慢的通用JavaScript代码中。

11
以下是来自Mozilla关于JavaScript严格模式的文章的引用:
JavaScript的灵活性使得在没有进行许多运行时检查的情况下,实际上不可能做到这一点。某些语言功能是如此普遍,以至于执行运行时检查会带来相当大的性能成本。一些严格模式的调整,再加上要求用户提交的JavaScript是严格模式代码,并以特定方式调用它,可以大大减少对那些运行时检查的需求。
上述引用清楚地表明,在使用严格模式时存在某些性能改进。
正如您所知,严格模式禁用了JavaScript之前提供的几个功能(其中大部分被认为是不良实践)。由于浏览器在使用严格模式时可以轻松抛出错误,因此它不必执行检查并为您假定代码更正。从而导致性能提升。

9
我想补充一下 Chetan 的回答。我会为您指向一个您之前问过的问题问题。最有可能这就是答案。
严格模式对您的代码做出了某些假设并添加了额外的检查。与普通模式相比,这些检查有两个与性能相关的影响:
  1. 需要额外的 CPU 时间进行检查
  2. 帮助编译器理解代码并更好地优化代码。
只要第二点胜过第一点,性能在严格模式下将得到改善。这适用于您的第一个函数。对于您的第二个函数,无法进行任何优化!!当编译器看到不安全的参数使用时,它会退出。因此,只剩下第一个影响。
我猜无法优化的代码会在严格模式下承受更多的惩罚。额外的检查没有意义。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接