草庐IT

javascript - 为什么在 JavaScript 中 while 和 do..while 之间存在巨大的时间差异

coder 2024-07-17 原文

while 循环

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

do..while 循环

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

所以 whiledo..while 之间的区别是,以编程方式 在 while 中,一个测试比 do while 执行多了

也就是

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

同理

如果从 1 到 50 的循环在 do..while 循环中执行一条语句,它将有 50 次测试(不会执行第 1 次测试)并且该语句将执行 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 毫秒

测试进行中

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 测量之间的区别在于测量开销 并且对测量本身(Heisenberg 任何人?)的影响如此之大计时非常快的功能(在 ms 标记旁边)容易出现测量错误。 真实但无关紧要

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

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

回顾一下到目前为止的发现:

  • 这不是 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.

关于javascript - 为什么在 JavaScript 中 while 和 do..while 之间存在巨大的时间差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44481131/

有关javascript - 为什么在 JavaScript 中 while 和 do..while 之间存在巨大的时间差异的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  6. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  7. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  8. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  9. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  10. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

随机推荐