【问题标题】:Why is this wrapped function call faster than a regular function?为什么这个包装函数调用比普通函数更快?
【发布时间】:2020-10-22 09:53:51
【问题描述】:

使用传统方法,在我的机器上执行这个递归斐波那契函数大约需要 9.5 秒:

const fib = n => {
  if (n == 1) return 0;
  if (n == 2) return 1;
  return fib(n - 1) + fib(n - 2);
};

console.log(fib(45));
➜ time node index.js
701408733
node index.js  9,50s user 0,04s system 99% cpu 9,566 total

但是,当我将函数体包装在另一个立即执行的函数中时,执行时间会下降到 7.5 秒:

const fib = n => {
  return (() => {
    if (n == 1) return 0;
    if (n == 2) return 1;
    return fib(n - 1) + fib(n - 2);
  })();
};

console.log(fib(45));
➜ time node index.js
701408733
node index.js  7,58s user 0,25s system 99% cpu 7,852 total

这是一个巨大的加速(~30%!),但我不明白为什么包装函数会产生这种差异。

【问题讨论】:

  • 也许值得使用 devtools 对其进行分析
  • 另外,也许每次都值得使用一个新的节点实例 - 事情变得温暖
  • 好主意。我试试看。
  • 你多久做一次这个测试?
  • 为什么一个运行9566次,另一个运行7852?

标签: javascript node.js


【解决方案1】:

这确实是一种很奇怪的行为。

经过一番挖掘,我发现在过去的几个问题(例如,this one)中提到,JS 在递归方面表现更差,例如在fib1

现在出现的问题是fib2 是否不是递归函数。我假设因为它创建了一个新的匿名函数——而不是使用对 fib2 的相同内存引用——它实际上是“递归更少”,因此性能更好。

编辑:在阅读this question 之后,看起来正在发生某种尾调用处理(fib2 使用尾递归,而不是 fib1,其中计算每个调用在返回结果之前)。

另一个有趣的观察是 - 在一定程度上 - 递归确实工作得更好,但它会以指数级速度变慢。

function fib1(n) {
  if (n === 1) return 0;
  if (n === 2) return 1;
  return fib1(n - 1) + fib1(n - 2);
};

function fib2(n) {
  return (() => {
    if (n === 1) return 0;
    if (n === 2) return 1;
    return fib2(n - 1) + fib2(n - 2);
  })();
};

function calculateAverageTime(func, iterations = 5) {
  let sum = 0;

  [...Array(iterations)].forEach(() => {
    const start = new Date();
    func();
    const finish = new Date();
    sum += (finish - start);
  });

  return Math.round(sum / iterations);
}

function compare(i) {
  const fib1RunTime = calculateAverageTime(() => fib1(i));
  const fib2RunTime = calculateAverageTime(() => fib2(i));
  console.log(`Difference for fib(${i}) is ${(fib2RunTime - fib1RunTime)}ms`);
}

[10, 20, 30, 40].forEach(i => compare(i));

【讨论】:

    【解决方案2】:
    return (() => {
    

    我不确定..但可能这个字符串在程序控制这一行之前等待响应:

    if (n == 1) 
    

    在程序开始控制“n”是否有效地“== 1”之前,程序会占用一小部分内存(堆栈),并将所有函数放入该部分内存中。

    然后启动函数。

    【讨论】:

      猜你喜欢
      • 2012-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-19
      • 1970-01-01
      • 2020-01-09
      • 2011-04-30
      相关资源
      最近更新 更多