为什么 while 和 do..while in JavaScript 之间存在巨大的时差
Why the huge time difference between while and do..while in JavaScript
while 循环
测试条件,如果为真,则执行代码
do..while 循环
第一次执行。然后测试执行。
所以while
和do..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');
inner
和 outer
测量之间的差异是 测量开销 并且对测量本身的影响(海森堡有人吗?)以至于计时非常快的功能(在 ms 标记旁边)容易出现测量错误。 真实但无关紧要
尝试在大循环中包装您的代码(例如重复 1000-100000 次)以减少测量的影响。 事实证明并非如此
根据上面的说法,长周期会有微小的测量差异,但测试表明差异与周期数成比例,因此是 NOT 只是测量开销。
回顾到目前为止的发现:
- 这不是
while
和do..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
while 循环
测试条件,如果为真,则执行代码
do..while 循环
第一次执行。然后测试执行。
所以while
和do..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');
inner
和 outer
测量之间的差异是 测量开销 并且对测量本身的影响(海森堡有人吗?)以至于计时非常快的功能(在 ms 标记旁边)容易出现测量错误。 真实但无关紧要
尝试在大循环中包装您的代码(例如重复 1000-100000 次)以减少测量的影响。 事实证明并非如此
根据上面的说法,长周期会有微小的测量差异,但测试表明差异与周期数成比例,因此是 NOT 只是测量开销。
回顾到目前为止的发现:
- 这不是
while
和do..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