【问题标题】:Why is the Array.prototype.find() polyfill slower than the ES6 implementation?为什么 Array.prototype.find() polyfill 比 ES6 实现慢?
【发布时间】:2019-04-05 11:05:05
【问题描述】:

我的印象是大部分 ES6 功能只是语法糖。但是,当我将 MDN 上的 find polyfill 与常规 ES6 实现进行比较时,它的速度似乎只有一半。究竟是什么解释了这种性能差异,难道不是在底层都一样吗?

基准测试请参考下面的sn-p:

// Find polyfill
function find(obj, predicate) {
  // 1. Let O be ? ToObject(this value).
  if (this == null) {
    throw new TypeError('"this" is null or not defined');
  }

  var o = Object(obj);

  // 2. Let len be ? ToLength(? Get(O, "length")).
  var len = o.length >>> 0;

  // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  if (typeof predicate !== 'function') {
    throw new TypeError('predicate must be a function');
  }

  // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
  var thisArg = arguments[1];

  // 5. Let k be 0.
  var k = 0;

  // 6. Repeat, while k < len
  while (k < len) {
    // a. Let Pk be ! ToString(k).
    // b. Let kValue be ? Get(O, Pk).
    // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
    // d. If testResult is true, return kValue.
    var kValue = o[k];
    if (predicate.call(thisArg, kValue, k, o)) {
      return kValue;
    }
    // e. Increase k by 1.
    k++;
  }

  // 7. Return undefined.
  return undefined;
}
const testArray = ["Hello", "Hi", "Good Morning", "Good Afternoon", "Good Evening", "Good Night"];

// Polyfill benchmark
console.time('findPolyfill');
for (var i = 0; i < 10000; i++) {
  find(testArray, (item) => item === "Hello")
}
console.timeEnd('findPolyfill');

// ES6 benchmark
console.time('find ES6');
for (var i = 0; i < 10000; i++) {
  testArray.find((item) => item === "Hello");
}
console.timeEnd('find ES6');

【问题讨论】:

  • 在 Chrome 上,运行 this 和运行已编译(仅半优化)Javascript 之间的区别。 Javascript 不是用 Javascript 编写的 :)

标签: javascript arrays performance ecmascript-6 v8


【解决方案1】:

本机版本可以利用内部优化和快捷方式,只要它们不能从外部观察到。如果没有编译机器代码,它也可能被预先优化并存储为至少字节码。 (取决于 JavaScript 引擎。)

相比之下,polyfill 是对规范所说的内容的一种非常迂腐的渲染,除非您在紧密循环中运行它超过 5-10k 次左右,否则不太可能被选择用于积极优化引擎。

有趣的是,您的循环设置为运行 10k 次,因此很可能在引擎优化它之前停止。或者引擎可能会在中途优化它——进一步延迟结果。例如,对我来说,下面的 polyfill 第一次运行时间约为 6 毫秒,但第二次和第三次运行时间约为 1.1 毫秒(Chrome v73 中的 V8 v7.3)。因此,显然它在第一次运行期间得到了优化(相反,这可能会减慢运行速度,但显然会加快后续运行速度)。

// Find polyfill
function find(obj, predicate) {
  // 1. Let O be ? ToObject(this value).
  if (this == null) {
    throw new TypeError('"this" is null or not defined');
  }

  var o = Object(obj);

  // 2. Let len be ? ToLength(? Get(O, "length")).
  var len = o.length >>> 0;

  // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  if (typeof predicate !== 'function') {
    throw new TypeError('predicate must be a function');
  }

  // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
  var thisArg = arguments[1];

  // 5. Let k be 0.
  var k = 0;

  // 6. Repeat, while k < len
  while (k < len) {
    // a. Let Pk be ! ToString(k).
    // b. Let kValue be ? Get(O, Pk).
    // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
    // d. If testResult is true, return kValue.
    var kValue = o[k];
    if (predicate.call(thisArg, kValue, k, o)) {
      return kValue;
    }
    // e. Increase k by 1.
    k++;
  }

  // 7. Return undefined.
  return undefined;
}
const testArray = ["Hello", "Hi", "Good Morning", "Good Afternoon", "Good Evening", "Good Night"];

function testPolyfill() {
    // Polyfill benchmark
    console.time('findPolyfill');
    for (var i = 0; i < 10000; i++) {
      find(testArray, (item) => item === "Hello")
    }
    console.timeEnd('findPolyfill');
}

function testNative() {
    // ES6 benchmark
    console.time('find ES6');
    for (var i = 0; i < 10000; i++) {
      testArray.find((item) => item === "Hello");
    }
    console.timeEnd('find ES6');
}

testPolyfill();
testNative();
testPolyfill();
testNative();
testPolyfill();
testNative();

【讨论】:

  • 我想我开始明白了。因此,与连续调用的 polyfill 相比,引擎在优化本机实现方面似乎要好得多。主要的性能差异是由于在 V8 引擎中执行 find 以及在引擎内完成的特定优化。谢谢你的解释!
  • @etarhan - 我认为 main 的性能差异是优化的与未优化的代码。在我使用 V8 的机器上,polyfill 在 5-6ms 之间开始,而原生的在 0.8ms 左右。但是一旦优化,polyfill 会在大约 1.15 毫秒内出现。但是,是的,本机版本将能够走捷径,而 polyfill 则不能。 :-)
猜你喜欢
  • 2014-02-09
  • 1970-01-01
  • 2017-08-22
  • 2018-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-22
相关资源
最近更新 更多