java for循环性能差异

l7mqbcuq  于 2021-06-29  发布在  Java
关注(0)|答案(2)|浏览(390)

我运行下面的简单程序,我知道这不是最好的方式来衡量性能,但结果让我惊讶,因此想在这里张贴问题。

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
o8x7eapl

o8x7eapl1#

在第一次迭代中,代码被解释运行。
在第二次迭代中,jit起了作用,在编译为本机代码时,它的速度减慢了一点。
在剩余的迭代中,本机代码运行得非常快。

yduiuuwa

yduiuuwa2#

因为winamp需要解码mp3的其他几帧,将其排入声音输出缓冲区。或者是因为月亮的相位发生了一些变化,你的动态背景需要改变,或者是因为东克罗伊登有人放屁,你的电脑被订阅了“伦敦的气味”推特。谁知道呢?
这不是性能测试的方式。你的cpu毕竟不是一台简单的机器;它有许多核心,每个核心都有管道和多个缓存层次结构。任何给定的核只能与其一个缓存进行交互,因此,如果一个核运行的指令对当前不在缓存中的内存进行操作,那么该核将关闭一段时间:它向内存控制器发送一个请求,请求将内存页加载到您需要访问的内存页中,然后等待它出现;这可能需要很多很多周期。
另一方面,你有一个操作系统,它在处理成千上万个进程和线程,其中许多是内核内部的,每次清空就像没有明天一样,并试图给对时间敏感的进程赋予额外的优先级,例如前面提到的winamp,它必须在声音缓冲区完全耗尽之前有机会解码更多的mp3帧,否则您会注意到跳过。这是不平凡的:在旧的windows上,你就是做不到这一点,这就是为什么旧的winamp是一个神奇的工程奇迹,或多或少地侵入windows以确保它得到它需要的优先权。那些日子已经过去很久了,但是如果你还记得的话,那么,你可以得出这样的结论:这不是小事,因此,操作系统在这些日子里总是带着偏见先发制人。
第三个重要的因素是jvm本身,它正在执行各种边界巫毒术,因为它既有一个热点引擎(对代码进行簿记,以便最终得出结论,花费大量的cpu资源来分析一个方法的错误,以便在优化的machinecode中重写它,因为该方法似乎占用了大量的cpu时间),又有一个垃圾收集器。
解决方法是完全忘记使用诸如测量currenttimemillis或nanotime和编写几个循环之类的简单方法来测量时间。这太复杂了,不可能真正起作用。
不,用jmh。

相关问题