JavaScript - 循环是否比逐行离散编写更快?

JavaScript - Are loops faster than discretely writing line-by-line?

忽略所有代码的整洁性和可读性,哪个脚本完成得更快?

这个:

for(var i = 0; i < 10; i++){
  --do that thing--
}

或者这样:

--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--
--do that thing--

或者它们在性能方面是否相同?

如果只是简单的计算语句,第二个版本更快。

但是当你可以循环它时,为什么要把代码写得如此丑陋和不可读(这取决于每行的语句)。

另一方面,代码将得到优化,您不会感觉到差异。

还要记住它是 JS。所以在某些情况下,语句可以是异步的 运行 没有顺序(不等待上一个语句结束)。

程序员不是代码循环机器,程序员必须编写一次并享受自动化。

which script will finish quicker

没有唯一有效的答案。

其他答案都是假设性的推测。

该标准不保证其中任何一个是 faster/slower,因此只能在给定机器上针对给定 ES 实现 运行 说明一些内容,并且只能在他们对其进行测量之后。但要注意,在微基准测试后获得任何合理的数字是困难的(大多数基准测试产生一些数字,但这些数字不一定意味着任何东西)。

这取决于您使用的 JS 引擎。循环展开是一种常见的优化技术,无需您的干预即可使用,因此它实际上取决于您的代码由哪个引擎解释。

通常你可以使用http://jsperf.com/测试特定代码在不同浏览器中的性能差异,但它似乎已经停机几天进行维护。相反,您可以手动将每个选择的代码放在它们自己的函数中,在测试开始时存储时间,调用该函数 10,000 次或其他,然后记录经过的时间。这是一个例子:

    function testEmpty(){ }
    function testLoop(){
        for(var i = 0; i < 10; i++){void i;}
    } function testUnrolled(){
        void 0;void 1;void 2;void 3;void 4;void 5;void 6;void 7;void 8;void 9;
    }
    function testLoop2(){
        for(var i = 0; i < 10; i++){void Date();}
    } function testUnrolled2(){
        void Date();void Date();void Date();void Date();void Date();
        void Date();void Date();void Date();void Date();void Date();
    }
    function testFunction(func, num){
        var a=Date.now();
        for(var i=0;i<num;i++){
            func();
        }
        return Date.now() - a;
    }
    console.log(testFunction(testEmpty,10000000)); // Took 9ms
    console.log(testFunction(testLoop,10000000)); // Took 112ms
    console.log(testFunction(testUnrolled,10000000)); // Took 42ms
    console.log(testFunction(testLoop2,10000)); // Took 705ms
    console.log(testFunction(testUnrolled2,10000)); // Took 714ms

保持浏览器不变(Firefox 47),这实际上表明性能变化与一种编码风格无关。事实上,取决于重复的内容会有所不同。

总的来说,将循环增加到 10 会对性能产生完全不明显的影响,因此通常差异可以忽略不计(并且性能可能不应该成为使用哪种样式的考虑因素)。

"Unrolling" 循环 "copy & pasting" 循环体可以 改进或降低 性能。

结果取决于...

  • ...您的 JavaScript 引擎
  • ...循环体中的代码
  • ...您的代码的文档化程度如何(不是开玩笑!)

让我们使用 Google 流行的 V8 JavaScript 引擎(Chrome,节点)分析性能:

普通循环:

var count = 0;
for (var i = 0; i < 10; i++) {
  count += 1;
}

展开的循环:

var count = 0;
count += 1;
count += 1;
... 
count += 1;

结果:展开的循环大约 快 10 倍

但是如果我们循环 1000 次而不是 10 次呢?然后展开的循环突然变得比普通循环慢 10 倍

如果我们用函数调用交换简单的算术表达式呢?

普通循环:

function f() {
  return 1;
}

var count = 0;
for (var i = 0; i < 10; i++) {
  count += f();
}

展开的循环:

var count = 0;
count += f();
count += f();
... 
count += f();

结果:展开的循环速度大约 50%

但是如果我们在函数 f 中添加注释呢?

function f() {
  // bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla
  return 1;
}

展开的循环突然慢了大约 20%

这是为什么?

  • V8 在解析主体包含少于 600 个字符(包括空格和注释)的函数时自动内联代码。
  • 当一个函数在一个很长的循环中 'stuck' 时,V8 执行 on-stack-replacement

关于最后一个例子:通过添加一个 > 600 字符的长注释,我们防止 V8 在解析期间内联函数 f 并依赖于 运行-time 优化功能(例如 'on stack replacement' ) 以整个函数和循环为目标,而不是手动重复代码。

如您所见,这种微"optimization"的结果很难预测。所以最好不要这样做 - 除非你针对特定 JS 引擎的特定版本。

有关性能分析,请参阅 https://jsfiddle.net/Lj9v7c2m/ - 您可能会得到不同的结果,具体取决于您的计算机/浏览器/版本。