我一直在阅读Java内存模型,我知道编译器可以重组语句以优化代码。
假设我有以下代码:
long tick = System.nanoTime();
function_or_block_whose_time_i_intend_to_measure();
long tock = System.nanoTime();
编译器是否会重新组织代码,以便在tick和tock之间不执行我想要测量的内容?例如,
long tick = System.nanoTime();
long tock = System.nanoTime();
function_or_block_whose_time_i_intend_to_measure();
如果是这样,保留执行顺序的正确方法是什么?
编辑: 说明使用nanoTime执行无序执行的示例:
public class Foo {
public static void main(String[] args) {
while (true) {
long x = 0;
long tick = System.nanoTime();
for (int i = 0; i < 10000; i++) { // This for block takes ~15sec on my machine
for (int j = 0; j < 600000; j++) {
x = x + x * x;
}
}
long tock = System.nanoTime();
System.out.println("time=" + (tock - tick));
x = 0;
}
}
}
输出上述代码:
time=3185600
time=16176066510
time=16072426522
time=16297989268
time=16063363358
time=16101897865
time=16133391254
time=16170513289
time=16249963612
time=16263027561
time=16239506975
在上面的例子中,第一次迭代中测量的时间明显低于后续运行中的测量时间。我认为这是由于乱序执行造成的。第一次迭代我做错了什么?
答案 0 :(得分:0)
编译器是否会重新组织代码,以便在tick和tock之间不执行我想要测量的内容?
不。那将从不发生。如果编译器优化搞砸了,那将是一个非常严重的错误。引用维基的声明。
运行时(在这种情况下,通常是指动态编译器,处理器和内存子系统)可以自由地引入任何有用的执行优化,只要保证隔离线程的结果完全是与在程序中发生的语句(也称为程序顺序)的顺序执行所有语句相同。
因此,只要结果与按程序顺序执行的结果相同,就可以进行优化。在您引用的情况下,我会假设优化是本地的,并且没有其他线程会对此数据感兴趣。这些优化是为了减少对主存储器的跳闸次数,这可能是昂贵的。当涉及多个线程并且他们需要了解彼此的状态时,您将只会遇到这些优化问题。
现在,如果2个线程需要一致地看到彼此的状态,它们可以使用volatile变量或内存屏障(synchronized)来强制将写入/读取序列化到主存储器。您可能感兴趣的Infoq ran a nice article on this。
答案 1 :(得分:0)
Java内存模型(JMM)在程序的所有操作上定义了一个名为happens-before
的部分排序。定义了七个规则以确保happens-before
排序。其中一个名为Program order rule
:
程序订单规则。线程中的每个操作都发生在该程序中稍后出现的该线程中的每个操作之前。
根据此规则,编译器不会重新排序您的代码。
本书Java Concurrency in Practice对这个主题给出了很好的解释。