为什么 while 和 do..while in JavaScript 之间存在巨大的时差

Why the huge time difference between while and do..while in JavaScript

while 循环

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

do..while 循环

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

所以whiledo..while之间的区别是,在程序上while,比while

多执行一个测试

也就是

如果从 1 到 50 的循环在 while 循环中执行一个语句,它将有 51 次测试(50 次正确和 1 次错误)并且该语句将执行 50 次。

同样

如果从1到50的循环在do..while循环中执行一条语句,则将进行50次测试(不会执行第1次测试)并且该语句将执行50次。

所以,只少了一个test/check。就是这样。

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

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

397.91 毫秒多 1 次额外测试

测试进行

Chrome (58.0.3029.110)

Edge 14

编辑:我有一个答案(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 次)以减少测量的影响。 事实证明并非如此

根据上面的说法,长周期会有微小的测量差异,但测试表明差异与周期数成比例,因此是 NOT 只是测量开销。

回顾到目前为止的发现:

  • 这不是whiledo..while的问题,因为颠倒这两个函数的顺序并不会颠倒时间:第一个总是较慢的;
  • 这不是测量开销的问题,因为差异是宏观比例的(它应该是一个变量,但很小——但它不是);
  • 它与渲染无关,因为我在某个时候完全删除了它;
  • inner-outer 代码片段表明,通过将 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');

Explanation: the JS engine compiles the source JS into native code on-the-fly. It has gradual performance scaling, but it can only compile a function AFTER it has returned. This means that the function is compiled and gradually optimized over a longer period of time. This, in fact, is a well known feature of V8. What is measured in the A-B scenario is not representative because of this edge condition (initial measures are inaccurate). The A-B-A-B-A scenario shows that A and B converge over time and measurements settle when they are far away from the edge (initial) condition.

实际上差别很小。由于在定时代码中调用 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