使用功能样式Javascript与"程序"的性能影响

ada*_*Lev 7 javascript performance functional-programming

有没有人做过基准测试,或者可以链接到关于这个主题的文章?特别感兴趣的是IE结果,因为通常JS性能在其他浏览器中不是问题.

我想知道做以下事情的速度有多慢:

var numbers = [1, 2, 3, 4, 5, 6, 7];
var results = numbers.map(function() { 
  // do some stuff
});
Run Code Online (Sandbox Code Playgroud)

而不是典型的:

var numbers = [1, 2, 3, 4, 5, 6, 7];
var results = [];

for (var i = 0; i < numbers.length; i++) {
  var number = numbers[i];
  var result;
  // do some stuff
  results.push(result);
}
Run Code Online (Sandbox Code Playgroud)

我显然更喜欢功能样式,但我认为为每个项目调用额外函数的额外开销可能会减慢大型集合的速度.

谢谢!

tur*_*yag 5

TL;DR:语法越旧,速度越快。当您优化速度时,请避免函数式编程。

我决定提出一个基准,其中包括您可能使用函数式编程执行的常见操作,它只是生成了一些聚合。可以在此处运行生成的基准测试。以下结果是 2020 年底的结果,但是当您阅读本文时,可能已经做了一些优化或某些操作,您可以使用此链接重新测试它,但如果该链接已失效,代码也粘贴在下面。我使用了 Chrome 的结果,但是虽然某些浏览器的运行速度比其他浏览器更快,但它们都保持相同的性能顺序。事实证明,在撰写本文时,Firefox 的速度要慢得多,例如 Chrome 比 Firefox 快 5 倍。Edge 与 Chrome 一样快。Safari 的速度也与 Chrome 一样快。Node 也和 Chrome 一样快。由于 Chrome、Safari 和 Node 都运行在 v8 引擎上,我怀疑它们将始终具有相同的速度:

https://jsbench.me/dvkgymlimu/1

第一个函数“aggregate_function”使用所有最新的语法、扩展运算符、reduce 等。它是迄今为止最慢的,运行速度约为 10M 操作/秒。考虑到这是最慢的,但每秒仍然疯狂运行 1000 万次,我认为这证明了除了最极端的情况之外的所有情况,这个论点都是没有意义的。如果每秒 1000 万次对您来说还不够快,请继续阅读。

下一个函数使用“of”循环,并且比函数版本快四倍。我并没有真正期待这种程度的性能提升。

最后一个函数“aggregate_imperative_idx”使用“旧的”JavaScript 东西,没有扩展运算符,没有“of”循环,只是一个索引循环。如果没有“let”和“const”关键字,它应该在 Netscape Navigator 上运行。我很高兴地发现使用“var”并没有提高性能。那会让我很难过。它最终比功能实现快 8.2 倍,达到 82M 操作/秒。



function aggregate_functional (numbers) {
  if (numbers.length == 0) {
    throw new Error("aggregate() requires at least one number")
  }
  const sum = numbers.reduce((s, n) => s += n)
  let min = Math.min(...numbers)
  let max = Math.max(...numbers)
  const average = sum / numbers.length
  return {
    sum,
    average,
    min,
    max,
  }
}

function aggregate_imperative (numbers) {
  if (numbers.length == 0) {
    throw new Error("aggregate() requires at least one number")
  }
  let sum = 0
  let min = numbers[0]
  let max = numbers[0]
  for (const val of numbers) {
    if (val < min) min = val
    if (val > max) max = val
    sum += val
  }
  const average = sum / numbers.length
  return {
    sum,
    average,
    min,
    max,
  }
}

function aggregate_imperative_idx (numbers) {
  if (numbers.length == 0) {
    throw new Error("aggregate() requires at least one number")
  }
  let sum = 0
  let min = numbers[0]
  let max = numbers[0]
  for (let i = 0; i < numbers.length; i++) {
    const val = numbers[i]
    if (val < min) min = val
    if (val > max) max = val
    sum += val
  }
  const average = sum / numbers.length
  return {
    sum,
    average,
    min,
    max,
  }
}
Run Code Online (Sandbox Code Playgroud)


ada*_*Lev 3

我不满足于缺乏关于这个主题的证据,因此写了一个简短的基准测试。它远非完美,但我认为它回答了问题。

我在 IE 8/win 中运行它,虽然函数方法速度较慢,但​​它永远不会成为实际代码中的瓶颈。(除非你正在做一些你不应该在客户端做的事情)

所以每当我必须选择时我都会使用更干净的方法(是的)

(5 种最佳)
功能方法:453ms
老式方法:156ms

Array.prototype.map = function(fun) {
  var len = this.length >>> 0;
  if (typeof fun != "function")
    throw new TypeError();

  var res = new Array(len);
  var thisp = arguments[1];
  for (var i = 0; i < len; i++) {
    if (i in this)
      res[i] = fun.call(thisp, this[i], i, this);
  }

  return res;
};

/**
 *
 *
 */

// Initialize test array
var numbers = [];
for (var i = 0; i < 100000; i++) numbers.push(i);

// Benchmark!
var start = +new Date();

// Test 1
var results1 = numbers.map(function(num) {
  return num + num;
});

alert('1. Functional map:' + (start - new Date()));
start = +new Date();

// Test 2
var results2 = [];
for (var j = 0, l = numbers.length; j < l; j++) {
  var num = numbers[j];
  results2.push(num + num)
}

alert('1. Old school approach' + (start - new Date()));
start = +new Date();
Run Code Online (Sandbox Code Playgroud)