javascript 循环(for 语句)在 21 亿次迭代后变慢了?

Aha*_*ebi 0 javascript for-loop v8 slowdown

我试图对 javascript 和 .net core 进行基准测试,以便选择一个服务器端框架来提供一些需要迭代大型数组(大约 21 亿)的特定 Restful 服务。在处理一个简单的代码时,我意识到 node 在特定的数字迭代后有奇怪的行为。我在多个平台上重复并达到了相同的结果。测试平台为:

  • macOS catalina (nodeJS v.12.18) intel core i9 4ghz 6 core
  • linux centos 7 (nodeJS v.12.18) vm intel core i9 4ghz 2 core
  • 谷歌浏览器版本 84.0.4147.105(官方版本)(64 位)
  • Mozilla Firefox 版本 78.2

运行视频显示出人意料地将处理时间从 300 毫秒增加到 600 毫秒大约两倍

示例代码:

1.节点:

var cnt = 0;
var logPeriod=100000000;
var max=10000000000;
for (let i = 0; i < max; i++) {
  if (i % logPeriod === 0) {
    // var end = Date.now();
    if (i !== 0) {
      console.timeEnd(cnt*logPeriod, i);
      cnt++;
    }
    console.time(cnt*logPeriod);
  }
}
Run Code Online (Sandbox Code Playgroud)

2.浏览器

<!DOCTYPE html>
<html>
  <head>
    <script>
      function doloop() {
        var cnt = 0;
        var logPeriod = 100000000;
        var max = 10000000000;
        for (let i = 0; i < max; i++) {
          if (i % logPeriod === 0) {
            // var end = Date.now();
            if (i !== 0) {
              console.timeEnd(cnt * logPeriod, i);
              cnt++;
            }
            console.time(cnt * logPeriod);
          }
        }
      }
    </script>
  </head>
  <body>
    <button onclick="doloop()">doloop</button>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

jmr*_*mrk 6

V8 开发人员在这里。

V8 的优化编译器生成的代码尽可能使用纯 32 位整数作为数字。一旦一个数字超过 int32 范围(或精度要求,即当它需要保存小数值时),那么这种优化的代码将被丢弃(或从不首先生成)并使用 64 位双精度值,作为 JavaScript 规范需要。算术运算(即使是像 一样简单的东西i++)在 64 位双精度上比在 32 位整数上慢,这正是硬件所做的。

在行为方面,这种内部差异是无法观察到的:数字总是表现得好像它们是 64 位双精度数。但这并不意味着引擎实际上总是在引擎盖下使用 64 位双精度数:正如您在此处看到的,当引擎可以在内部使用 32 位整数时,会带来显着的性能优势。

选择 [JavaScript or .net for] 需要迭代大型数组(约 21 亿)的 restful 服务

这是一个简单的决定:使用 .net。V8(以及 Node)不允许您创建包含 21 亿个元素的数组,因为每个对象的大小限制远低于此限制。当然,var a = new Array(2_100_000_000)会评估得很好,但那是因为它实际上并没有分配所有的内存。开始填充元素并在一段时间后观看它崩溃:-)

如果您的实际阵列毕竟不会那么大,那么请定义一个更接近您实际工作负载的基准,因为它的结果将更具代表性,因此对您的决策更有用。

  • @AlanLiang:你不能增加V8的每个对象的大小限制。预计不同的发动机有不同的限制。它们可能是自我强加的任意限制,也可能来自内部实现设计选择。V8 仅使用 30 位来表示对象的大小,因此每个对象必须适合 1G。我们更喜欢这种方式,因为(1)使用更多位会增加元数据开销,(2)在大多数情况下,作为用户,您甚至不希望单个对象使用比这更多的内存。FWIW,TypedArrays 可以变得更大。 (2认同)