【问题标题】:Why the huge time difference between while and do..while in JavaScript为什么 JavaScript 中的 while 和 do..while 之间存在巨大的时间差异
【发布时间】:2017-11-12 20:22:56
【问题描述】:

while 循环

测试条件,如果为真,则执行代码

do..while 循环

第一次执行。然后测试并执行。

所以whiledo..while 之间的区别在于,以编程方式在while 中,比在while 中执行的测试更多

那是

如果一个从 1 到 50 的循环在 while 循环中执行一个语句,它将有 51 个测试(50 个真和 1 个假),该语句将执行 50 次。

同样

如果在 do..while 循环中执行一个从 1 到 50 的循环,其中只有一条语句,它将进行 50 次测试(不进行第一次测试),该语句将执行 50 次。

因此,只需减少一项测试/检查。就是这样。

但是当我测试执行所花费的时间时,它显示出很大的差异。

function whileFn() {
  var i = 0;
  while (i < 10) {
    document.write(i);
    i++;
  }
}

function doWhileFn() {
  var i = 0;
  do {
    document.write(i);
    i++;
  } while (i < 10)
}

console.time('whileFn');
whileFn();
console.timeEnd('whileFn');

document.write('<br/>');

console.time('doWhileFn');
doWhileFn();
console.timeEnd('doWhileFn');

如您所见 on the image 和代码示例,while 循环耗时 15 毫秒,而 do while 仅耗时 5 毫秒。

这种巨大差异背后的原因是什么?

测试 10 个元素

按照@pid 的建议进行更新

测试 1000

1 次额外测试花费了 23 毫秒

测试 10000

1 次额外测试多出 397.91 毫秒

进行测试

铬 (58.0.3029.110)

边缘 14

【问题讨论】:

    标签: javascript while-loop do-while execution-time


    【解决方案1】:

    编辑:我有一个答案(TL;DR:跳到最后)

    我自己做了一些测试。

    function whileFn() {
      var i = 0;
      while (i < 10) {
        document.write(i);
        i++;
      }
    }
    
    function doWhileFn() {
      var i = 0;
      do {
        document.write(i);
        i++;
      } while (i < 10)
    }
    
    
    console.time('doWhileFn');
    doWhileFn();
    console.timeEnd('doWhileFn');
    
    document.write('<br/>');
    
    console.time('whileFn');
    whileFn();
    console.timeEnd('whileFn');

    我把这两个函数倒过来了,时间还是一样的。 也就是说,第一个总是比第二个慢。 这证明循环没有任何意义,它完全受渲染引擎的约束(渲染无关)

    如果你完全删除document.write(),差异会进一步缩小。 (无关)

    要正确测量时间,你必须考虑到时间本身的测量,实际上这显示了测量时间的开销:

    console.time('outer');
    console.time('inner');
    for (var i = 0; i < 10; i++);
    console.timeEnd('inner');
    console.timeEnd('outer');

    innerouter 测量之间的区别在于 测量开销,并且对测量本身(海森堡有人吗?)的影响如此之大,以至于计时非常快(在 ms 标记旁边) ) 容易出现测量误差。 真实但不相关

    尝试将您的代码封装在巨大的循环中(例如重复 1000-100000 次)以减少测量的影响。 事实证明并非如此

    根据上述说法,长周期会有微小的测量差异,但测试表明,差异会随着周期数的增加而变化,因此只是一个测量开销。

    回顾迄今为止的发现:

    • 这不是whiledo..while的问题,因为颠倒两个函数的顺序并不会颠倒时序:第一个总是较慢的;
    • 这不是测量开销的问题,因为差异可以缩放到宏观比例(它应该是一个变量,但数量很小 - 但事实并非如此);
    • 这与渲染无关,因为我在某些时候已经完全删除了它;
    • inner-outer sn-p 表明,通过将 10 替换为大量数字,长周期的测量开销很小,但问题中的原始代码并非如此 - - 这里的差异与周期数成正比。

    编辑:结论

    这是一个交替测试。测量 A、B、A,再测量 B,最后再测量 A:你越往前走,它就越收敛

    证明:

    function whileFn() {
      var i = 0;
      while (i < 10) {
        document.write(i);
        i++;
      }
    }
    
    function doWhileFn() {
      var i = 0;
      do {
        document.write(i);
        i++;
      } while (i < 10)
    }
    
    
    console.time('doWhileFn');
    doWhileFn();
    console.timeEnd('doWhileFn');
    
    document.write('<br/>');
    
    console.time('whileFn');
    whileFn();
    console.timeEnd('whileFn');
    
    document.write('<br/>');
    
    console.time('doWhileFn');
    doWhileFn();
    console.timeEnd('doWhileFn');
    
    document.write('<br/>');
    
    console.time('whileFn');
    whileFn();
    console.timeEnd('whileFn');
    
    document.write('<br/>');
    
    console.time('doWhileFn');
    doWhileFn();
    console.timeEnd('doWhileFn');

    说明: JS 引擎即时将源 JS 编译为本机代码。它具有逐步的性能扩展,但它只能在函数返回后编译它。这意味着函数会在较长时间内编译并逐渐优化。事实上,这是 V8 的一个众所周知的特性。由于这种边缘条件(初始测量不准确),在 A-B 场景中测量的内容不具有代表性。 A-B-A-B-A 场景显示 A 和 B 随着时间的推移收敛,并且当它们远离边缘(初始)条件时,测量值稳定。

    【讨论】:

    • +1 进行解释。但是根据您的第一个答案,我以不同的顺序对 1000 和 10000 进行了测试。请检查问题中的图像。它显示出巨大的差异。 10K 时约为 400 毫秒
    • @SagarV JS 中的时间测量不准确。对于同一段代码,不同的系统负载可能会在 JS 中产生不同的时间差。例如,如果我设置了一个超时,然后在空载时运行一次,另一次在 100%cpu 上运行(渲染视频)等等。对于这两种情况,超时不会以完全相同的时间间隔触发。更准确地说,有一些东西叫做 process.hrtime()。这可能会有所帮助:stackoverflow.com/questions/11725691/…
    • @SagarV 是的,我明白了!这很好奇。尽管如此,颠倒顺序确实会改变测量,所以这与whiledo..while 无关。这完全是另一回事,但我现在很困惑:)
    • 这和V8v5.9一样吗(因为解释和编译的过程已经改变了,有涡扇和点火)?
    • @LarsW 是的,流程并没有太大变化,结论(优化受执行影响)保持不变。
    【解决方案2】:

    实际上差别很小。由于在定时代码中调用了 document.write(),所以会出现感知到的问题。以下简单代码显示 do 和 while 之间没有显着差异:

    let REPEATS = 1e7;
    
    function play() {
        let start = undefined;
        let count = undefined;
        let elapsedWhile = undefined;
        let elapsedDo = undefined;
        let x = undefined;
    
        start = Date.now();
        count = REPEATS;
        while(count >0) {
            x = Math.sqrt(Math.random());
            count -= 1;
        }
        elapsedWhile = Date.now() - start;
    
        start = Date.now();
        count = REPEATS;
        do {
            x = Math.sqrt(Math.random());
            count -= 1;
        }while(count >0);    
        elapsedDo = Date.now() - start;
    
        console.log(elapsedWhile, elapsedDo);
    
    }
    
    function init() {
        xreport ('OK');
        var formx = document.getElementById("f1");;
        formx.X.addEventListener('click', play);
    }
    

    在这里试试:http://www.johnwheater.net/JAVASCRIPT/PLAY3/html/main.html

    【讨论】:

      猜你喜欢
      • 2014-01-11
      • 1970-01-01
      • 1970-01-01
      • 2011-07-31
      • 2010-10-25
      • 2013-05-21
      • 1970-01-01
      • 2011-04-07
      • 1970-01-01
      相关资源
      最近更新 更多