在啥情况下 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 在文档中更常见的原因 您的代码将无法编译,因为一旦您离开循环体,expect
和 update
就会超出范围。不过,在循环之前声明这两个变量是可行的,所以你的观点仍然有效。【参考方案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) 我们都知道while
和 do while
是什么意思。 2)你不能从字节码中推断出任何关于代码效率的东西。 JIT 编译器是进行重大优化的地方。 (即使是具有相同字节码序列的方法也可能执行不同的......由于 JIT 编译器使用来自解释器的运行时统计信息的方式。)以上是关于在啥情况下 do-while 可以比 while 更有效率?的主要内容,如果未能解决你的问题,请参考以下文章