【问题标题】:Exceptionally slow Javascript loop异常缓慢的 Javascript 循环
【发布时间】:2013-07-09 03:17:00
【问题描述】:

这在某种程度上是我的previous question 的后续。

我创建了a jsPerf,它比较了获取 RGB 像素值的一维数组的多种方法

var rgb = [R, G, B, R, G, B...]

并将这些转换为 HTML5 画布的 RGBA 值(其中 alpha 通道始终为 255,完全不透明)。

var rgba = [R, G, B, 255, R, G, B, 255...]

在我的测试中,我发现我测试的一个名为“For Loop”的循环比其他循环慢得多。在其他循环完成操作每秒数亿次的情况下,它以每秒高达 86 次的速度完成。该循环可以在上面的 jsPerf 链接中找到,但这里有一些带有“For Loop”和“4*unrolled,skip alpha”的代码,这是测试中更快的循环之一。

//Setup for each test
function newFilledArray(length, val) {
    var array = Array(length);
    for (var i = 0; i < length; i++) {
        array[i] = val;
    }
    return array;
}

var w = 160;  //width
var h = 144;  //height

var n = 4 * w * h; //number of length of RGBA arrays
var s = 0, d = 0;  //s is the source array index, d is the destination array index

var rgba_filled = newFilledArray(w*h*4, 255);  //an RGBA array to be written to a canvas, prefilled with 255's (so writing to the alpha channel can be skipped
var rgb = newFilledArray(w*h*3, 128);  //our source RGB array (from an emulator's internal framebuffer)

//4*unrolled, skip alpha - loop completes (exits) 185,693,068 times per second
while (d < n) {
    rgba_filled[d++] = rgb[s++];
    rgba_filled[d++] = rgb[s++];
    rgba_filled[d++] = rgb[s++];
    d++;
}

//For Loop - loop completes (exits) 85.87 times per second
for (var d = 0; d < n; ++d) {
    rgba_filled[d++] = rgb[s++];
    rgba_filled[d++] = rgb[s++];
    rgba_filled[d++] = rgb[s++];
}

它怎么会在语法上如此相似,但在性能方面却相去甚远?

【问题讨论】:

  • 你有没有试过切换循环的顺序
  • 您是否尝试过使用dis()
  • @aaronman 你是什么意思? jsperf 在全新的 JS 环境中运行每个测试。
  • 没看问题就看代码了,我的坏@Barmar
  • 第一个指标表明这些基准不太对劲,如果整个 while 循环真的每秒运行那么多次,那意味着它执行了大约 290 万亿 每秒操作数!那必须是浏览器/ CPU 组合的一大特色。因此,并不是 for 循环异常缓慢(不是;它每秒执行大约 1.34 亿次操作),而是 while 循环显示的速度快得离谱。

标签: javascript performance loops


【解决方案1】:

只有for 循环这么慢的原因是因为它是唯一正确的测试用例;所有其他测试用例从不重置,其中包括d 的值,所以第一次迭代是正常的,其余的显然超级快:)

jsperf 给出了更好的结果,其中 for 循环仅比最快的结果稍慢。

更新

正如bfavaretto 建议的那样,您还应该重置s 和您正在构建的目标数组以获得更一致的结果。他的结果可以在here找到。

【讨论】:

  • 我认为他假设在每次测试迭代之前都重新运行了准备代码。
  • @Barmar 这也是我的猜测 :)
  • 好收获!让我感到困惑的是,设置声明它“在每个计时测试循环之前运行,在计时代码区域之外”。这个措辞仍然让人觉得 setup 应该像我想象的那样运行!
  • @TreyKeown 如果你还重置了s 和目标数组,你会在所有测试中获得更多的操作/秒(以及更一致的最终状态)。 jsperf.com/rgb-to-rgba/9
  • @bfavaretto 谢谢!我接受了您的修订并对其进行了更新,以便一切都真正得到重置。希望这个测试的最终版本可以在这里找到:jsperf.com/rgb-to-rgba/11
猜你喜欢
  • 2017-05-12
  • 2018-11-11
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-01
相关资源
最近更新 更多