2013-03-25 66 views
4

我一直在阅读Java内存模型,并且我知道编译器可以重新组织语句以优化代码。测量时间间隔和乱序执行

假设我有以下代码:

long tick = System.nanoTime(); 
function_or_block_whose_time_i_intend_to_measure(); 
long tock = System.nanoTime(); 

将编译器重新组织过的代码的方式,我打算来衡量是不是蜱和滴答之间执行?例如,

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 

在上述例子中,在第一次迭代中所测量的时间是显著低于后续运行中的测量时间。我认为这是由于无序执行。第一次迭代我做了什么错误?

+2

我的直觉是,Java不会重新安排非内联函数调用,因为它无法在一般意义上知道它们可能具有哪些副作用(特别是如果函数位于不同的编译单元中)。但我不能支持任何事情。 – Patashu 2013-03-25 03:45:13

+0

这可能是一个系统调用,它超出了Java内存模型的范围。 – ZhongYu 2013-03-25 04:25:52

回答

0

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

都能跟得上。那从来没有发生。如果编译器优化混乱了,这将是一个非常严重的错误。引用wiki的声明。

只要保证隔离线程的结果,运行时(在本例中通常指的是动态编译器,处理器和内存子系统)可以自由引入任何有用的执行优化。与在程序中发生的声明(也称为程序顺序)中执行的所有声明完全相同。

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

现在如果2个线程需要一致地看到彼此的状态,他们可以使用易失性变量或内存屏障(同步)来强制将写入/读取序列化到主内存。您可能会感兴趣的Infoq ran a nice article on this

0

Java存储器模型(JMM)定义了一个名为happens-before的部分排序,用于程序的所有操作。定义了七条规则以确保订购happens-before。其中之一称为Program order rule

程序订单规则。线程中的每个动作都会在该线程中的每个动作之前发生,该动作稍后将按程序顺序进行。

根据这条规则,你的代码不会被编译器重新排序。

Java Concurrency in Practice给出了一个关于这个话题的很好的解释。

+0

我还是不明白。假设我有以下代码: 'long x,y; x = 10; // S2 y = 20; // S3 x = x + x; // S4' 编译器优化代码以使S2和S4依次发生,然后执行S3会不会更好。这也是安全的,因为S3不依赖于S2或S4。通过这样做,可以完成S4的计算,而x仍然在S2的寄存器中。我认为这是乱序执行的重点。 现在假设我用'y = System.nanoTime();'替换了S3。是否有一个“发生之前”的规则解释了这一点? – 2013-03-26 05:52:37