【问题标题】:Why the second operation is faster than the first?为什么第二次操作比第一次快?
【发布时间】:2019-04-30 15:13:42
【问题描述】:

我正在尝试查看 for 和 reduce 在数值数组上的性能差异,我可以看到 always 我正在测量的第二个函数(无论是什么,for 或 reduce)更快比第一个。我猜这与使用节点的数据缓存或线程池大小有关。这是代码:

process.env.UV_THREADPOOL_SIZE = 1;

let array = [
  1,
  23,
  4,
  5,
  6,
  7,
  8,
  7,
  65,
  4,
  3,
  23,
  43,
  2,
  23,
  32,
  23,
  23,
  234,
  243,
  423,
  432,
  43,
  23,
  2,
  23,
  2,
  23,
];

let sum = 0;
console.time('reduce');
sum = array.reduce((s, p) => (s += p), 0);
console.timeEnd('reduce');

sum = 0;
console.time('for');
for (let i = 0; i < array.length; i++) {
  sum += array[i];
}
console.timeEnd('for');

这段代码显示了不同的结果:

process.env.UV_THREADPOOL_SIZE = 1;

let array = [
  1,
  23,
  4,
  5,
  6,
  7,
  8,
  7,
  65,
  4,
  3,
  23,
  43,
  2,
  23,
  32,
  23,
  23,
  234,
  243,
  423,
  432,
  43,
  23,
  2,
  23,
  2,
  23,
];

let sum = 0;
console.time('for');
for (let i = 0; i < array.length; i++) {
  sum += array[i];
}
console.timeEnd('for');

sum = 0;
console.time('reduce');
sum = array.reduce((s, p) => (s += p), 0);
console.timeEnd('reduce');

我的意思是,如果你颠倒执行顺序,测量的结果就会不同。

为了进行测试,我使用的是 node v11.11.0

有什么想法吗?

编辑:我不是在寻找解释为什么 reduce 比 for 或类似的东西更快。我想知道为什么 nodejs 会产生这样的操作序列。

【问题讨论】:

  • Regular for 应该总是更快,因为它不需要实例化回调并检查是否有要处理的元素。
  • 我认为他的意思是第二个选项总是更快,即使他切换它们。
  • @Wendelin 不,for 总是更快:prntscr.com/nir8zg
  • 您的基准测试可能存在缺陷。在执行之前预热这两个函数。

标签: javascript node.js ecmascript-6


【解决方案1】:

我的意思是,如果你颠倒执行顺序,测量的结果就会不同。

这意味着:您的测试存在某种缺陷或结果非常随机,以至于您无法根据它们做出判断。

更频繁地运行测试(几千次),然后取平均时间(通过它平均其他代码片段的影响(您在多线程机器上运行),然后强制引擎选择它最强大的优化)。

在此之前,无法判断其中一个是否更快。结果很可能是:没关系,两者都足够快。

值得一读: Which is faster? - Eric Lippert

【讨论】:

    【解决方案2】:

    经过测试两者之间没有很大的时间差异。

    在链接中,你可以修改执行次数来测试它。

    https://repl.it/@statefull/TrustworthyDefiniteArraylist

    经过一些测试,问题出在console.time函数上。

    看这个:

    https://repl.it/@statefull/WrathfulCostlyIrc

    第一次调用console.time 需要更多时间。每次执行都会与Date.now 进行比较。

    更多测试表明,直到第一个 console.timeEnd 之前,第一个 console.timeEnd 的时间测量都不是真实的。

    见: https://repl.it/@statefull/SoggyLimegreenUser

    【讨论】:

      【解决方案3】:

      为reduce 的每次迭代添加一个函数到堆栈(() =&gt; {} 是一个新的函数调用)。这些函数调用为整个过程增加了一点额外的时间。

      只要时间限制不是很严格或数组很大,提高可读性通常是值得的。

      【讨论】:

      • 您在阅读for 循环时遇到困难?使用reduce 的原因是它的抽象级别更高,而不一定是为了更好的可读性。
      • @NirLevy:好的。那这就是应该说的,嗯?
      • 我在阅读 for 循环时没有问题,但是 reduce/map/forEach/filter/find 似乎更容易阅读和测试。有两件事可以简单,而一件仍然可以更简单。
      【解决方案4】:

      这在脚本化或 JIT 编译语言中很常见,并且与编译器的开销会减慢您的操作速度有关,但这只是第一次。在第一次之后,您的调用将编译代码,而不是编译脚本,但这取决于执行引擎的实现方式。这就是为什么测试通常要求您做某事不是一次,而是数次(理想情况下是数千次)

      【讨论】:

        【解决方案5】:

        Map/Reduce/Filter/Find 很慢,因为它们有回调函数,这些会增加开销

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-09-13
          • 2021-11-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多