我运行下面的简单程序,我知道这不是最好的方式来衡量性能,但结果让我惊讶,因此想在这里张贴问题。
public class findFirstTest {
public static void main(String[] args) {
for(int q=0;q<10;q++) {
long start2 = System.currentTimeMillis();
int k = 0;
for (int j = 0; j < 5000000; j++) {
if (j > 4500000) {
k = j;
break;
}
}
System.out.println("for value " + k + " with time " + (System.currentTimeMillis() - start2));
}
}
}
多次运行代码后的结果如下所示。
for value 4500001 with time 3
for value 4500001 with time 25 ( surprised as it took 25 ms in 2nd iteration)
for value 4500001 with time 0
for value 4500001 with time 0
for value 4500001 with time 0
for value 4500001 with time 0
for value 4500001 with time 0
for value 4500001 with time 0
for value 4500001 with time 0
for value 4500001 with time 0
所以我不明白为什么第二次迭代用了25ms,而第一次用了3ms,后来用了0ms,还有为什么在运行代码时总是用第二次迭代。
如果我将开始和结束时间打印移到外部forloop之外,那么我得到的结果是
for value 4500001 with time 10
2条答案
按热度按时间o8x7eapl1#
在第一次迭代中,代码被解释运行。
在第二次迭代中,jit起了作用,在编译为本机代码时,它的速度减慢了一点。
在剩余的迭代中,本机代码运行得非常快。
yduiuuwa2#
因为winamp需要解码mp3的其他几帧,将其排入声音输出缓冲区。或者是因为月亮的相位发生了一些变化,你的动态背景需要改变,或者是因为东克罗伊登有人放屁,你的电脑被订阅了“伦敦的气味”推特。谁知道呢?
这不是性能测试的方式。你的cpu毕竟不是一台简单的机器;它有许多核心,每个核心都有管道和多个缓存层次结构。任何给定的核只能与其一个缓存进行交互,因此,如果一个核运行的指令对当前不在缓存中的内存进行操作,那么该核将关闭一段时间:它向内存控制器发送一个请求,请求将内存页加载到您需要访问的内存页中,然后等待它出现;这可能需要很多很多周期。
另一方面,你有一个操作系统,它在处理成千上万个进程和线程,其中许多是内核内部的,每次清空就像没有明天一样,并试图给对时间敏感的进程赋予额外的优先级,例如前面提到的winamp,它必须在声音缓冲区完全耗尽之前有机会解码更多的mp3帧,否则您会注意到跳过。这是不平凡的:在旧的windows上,你就是做不到这一点,这就是为什么旧的winamp是一个神奇的工程奇迹,或多或少地侵入windows以确保它得到它需要的优先权。那些日子已经过去很久了,但是如果你还记得的话,那么,你可以得出这样的结论:这不是小事,因此,操作系统在这些日子里总是带着偏见先发制人。
第三个重要的因素是jvm本身,它正在执行各种边界巫毒术,因为它既有一个热点引擎(对代码进行簿记,以便最终得出结论,花费大量的cpu资源来分析一个方法的错误,以便在优化的machinecode中重写它,因为该方法似乎占用了大量的cpu时间),又有一个垃圾收集器。
解决方法是完全忘记使用诸如测量currenttimemillis或nanotime和编写几个循环之类的简单方法来测量时间。这太复杂了,不可能真正起作用。
不,用jmh。