测量时间间隔和乱序执行

我一直在阅读有关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 

在上面的示例中,第一次迭代中测量的时间明显低于后续运行中的测量时间。 我以为这是由于乱序执行造成的。 第一次迭代我做错了什么?

编译器是否会重新组织代码,以便在tick和tock之间不执行我想要测量的内容?

不。 这永远不会发生。 如果编译器优化搞砸了,那将是一个非常严重的错误。 引用维基的声明。

运行时(在这种情况下,通常是指动态编译器,处理器和内存子系统)可以自由地引入任何有用的执行优化,只要隔离线程的结果保证与它完全相同即可。本来所有的陈述都是按照程序中发生的语句的顺序执行的(也称为程序顺序)。

因此,只要结果与在程序顺序中执行时的结果相同,就可以进行优化。 在您引用的情况下,我会假设优化是本地的,并且没有其他线程会对此数据感兴趣。 这些优化是为了减少对主存储器的跳闸次数,这可能是昂贵的。 当涉及多个线程并且他们需要了解彼此的状态时,您将只会遇到这些优化的问题。

现在,如果2个线程需要一致地看到彼此的状态,它们可以使用volatile变量或内存屏障(synchronized)来强制将写入/读取序列化到主存储器。 Infoq发表了一篇很有意思的文章 ,可能会引起您的兴趣。

Java内存模型(JMM)定义了在程序的所有操作happens-before调用happens-before partial的部分排序。 定义了七个规则以确保happens-before订购happens-before 。 其中一个称为Program order rule

程序订单规则。 线程中的每个操作都发生在该程序中稍后出现的该线程中的每个操作之前。

根据此规则,编译器不会重新排序您的代码。

“ Java Concurrency in Practice ”一书对该主题给出了很好的解释。