在啥情况下 do-while 可以比 while 更有效率?

Posted

技术标签:

【中文标题】在啥情况下 do-while 可以比 while 更有效率?【英文标题】:In what situations can do-while be more efficient than while?在什么情况下 do-while 可以比 while 更有效率? 【发布时间】:2013-05-02 12:15:38 【问题描述】:

while 与 do-while

while 和 do-while 在功能上是等效的当块为空时,虽然 while 看起来更自然:

do  while (keepLooping());
while (keepLooping()) 

带有空块的 while/do-while 的一个典型用例是使用 compareAndSet (CAS) 强制更新原子对象。例如,下面的代码将以线程安全的方式递增a

int i;
AtomicInteger a = new AtomicInteger();
while (!a.compareAndSet(i = a.get(), i + 1)) 

上下文

java.util.concurrent 的几个部分使用do while (...) 惯用语进行 CAS 操作,ForkJoinPool 的 javadoc 解释说:

不寻常的do while (!cas...) 多次出现,这是强制更新 CAS 变量的最简单方法。

既然他们承认这是不寻常的,我想他们的意思是最好的而不是最简单的

问题

在某些情况下do while (!cas) 可以比while (!cas) 更高效,原因是什么?

【问题讨论】:

也许这只是历史偏好。无论如何,为什么要使用 ---while (condition); 是我要写的。 @MarkoTopolnik 同意;。我找到了this video,Doug Lea 在 57:00 左右说:“不要使用 while,使用 do-while,因为安全点”,幻灯片提到“较小的比赛窗口”。我想他指的是 GC 安全点,尽管我看不出它在这里有何不同。 Doug 对此非常省略 :) 我可以解释它的最佳方式不是关于 do-while 与 while,而是关于使用循环体进行赋值与将所有内容都塞进条件中。 @MarkoTopolnik 这也是我所理解的——但是为什么会出现麻烦呢?我想 SO 不是最好的提问地点! 是的...这对 SO “没有建设性”,因为地球上只有大约三个人可以给出超出猜测的答案:) 【参考方案1】:

所以“do while”意味着它将在 while 循环中运行代码一次。然后,它只在条件为真时运行 while 循环内的代码。

简单演示

boolean condition = false;

do
  System.out.Println("this text displayed");
while(condition == true);

输出“此文本显示”

正常

while(condition == true)
 System.out.Println("this text displayed");

输出“”

*由于条件为假,没有显示输出。

为什么或在哪里你会使用 do while,我没有遇到需要,所以我无法帮助你。这只是识别问题/需求,并使用你所知道的来解决它的问题。类似于乐高 - 机械类型不是“积木”。

【讨论】:

【参考方案2】:

在某些情况下,expect 和 update 的计算非常复杂,以至于在调用 compareAndSet 的同一行中可读。 然后你可以让它在 do 中更具可读性:

do 
  int expect = a.get();
  int update = expect + 1;
 while (!a.compareAndSet(expect, update));

【讨论】:

该问题假定为空块,因此 while 和 do-while 是严格等价的。 啊哈。也许这更像是它 看这个例子:grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… - 我认为这不是问题。 在大多数情况下,我同意 while (cas);更好。我只展示了 do while (!cas) 比 while (!cas) 更具可读性的示例。这就是为什么 do while 在文档中更常见的原因 您的代码将无法编译,因为一旦您离开循环体,expectupdate 就会超出范围。不过,在循环之前声明这两个变量是可行的,所以你的观点仍然有效。【参考方案3】:

这不是效率问题。有些情况没有 dowhile() 就无法解决。看看 java.util.Random.next(int bits)。如果您尝试对 while() 执行相同操作,则会出现代码重复,因为循环体必须在条件之前执行一次。

我已经问过一个非常相似的问题:compiling loops in Java。

这段代码:

public class Test 

    static int i = 0;

    public static void main(String[] args) 
        method1();
        method2();
    

    public static void method2() 
        dowhile(++i < 5);
    

    public static void method1() 
        while(++i < 5);
    

编译成:

public static void method2();
  Code:
   0:   getstatic       #4; //Field i:I
   3:   iconst_1
   4:   iadd
   5:   dup
   6:   putstatic       #4; //Field i:I
   9:   iconst_5
   10:  if_icmplt       0
   13:  return

public static void method1();
  Code:
   0:   getstatic       #4; //Field i:I
   3:   iconst_1
   4:   iadd
   5:   dup
   6:   putstatic       #4; //Field i:I
   9:   iconst_5
   10:  if_icmpge       16
   13:  goto    0
   16:  return

您可能会注意到 method1() 中第 13 行的附加指令。但正如我的问题中的回答所建议的那样,当由 JIT 编译成机器指令时,这没有任何区别。非常难以捉摸的性能改进。任何证明它的方法都必须使用 PrintAssembly 键运行。理论上method2更快,但实际上它们应该是相等的。

【讨论】:

你没有仔细阅读这个问题。根本没有循环体,效率问题非常低级,涉及最终的本机代码顺序。 我刚刚看了看组装,没有什么让我印象深刻的。 什么意思?都是一样的吗? 只有两条指令出现顺序不同,其余完全一致。 为什么人们认为反汇编的字节码是有益的? 1) 我们都知道 whiledo while 是什么意思。 2)你不能从字节码中推断出任何关于代码效率的东西。 JIT 编译器是进行重大优化的地方。 (即使是具有相同字节码序列的方法也可能执行不同的......由于 JIT 编译器使用来自解释器的运行时统计信息的方式。)

以上是关于在啥情况下 do-while 可以比 while 更有效率?的主要内容,如果未能解决你的问题,请参考以下文章

for while do-while三种循环体的特点和缺点

在啥情况下,AVX2 收集指令会比单独加载数据更快?

是否存在必须使用 while/do-while 而不是 for 的情况?

scanf与cin在啥情况下使用

do-while循环

Java——循环