Java:为什么第一次调用方法的速度较慢?

时间:2015-07-09 10:02:48

标签: java performance performance-testing

最近,我正在使用Java编写插件,发现第一次从get()检索元素(使用HashMap)非常慢。最初,我想问一个问题并找到this(虽然没有答案)。然而,通过进一步的实验,我注意到这种现象发生在ArrayList,然后是所有方法。

以下是代码:

public class Test {
    public static void main(String[] args) {
        long startTime, stopTime;

        // Method 1
        System.out.println("Test 1:");
        for (int i = 0; i < 20; ++i) {
            startTime = System.nanoTime();
            testMethod1();
            stopTime = System.nanoTime();
            System.out.println((stopTime - startTime) + "ns");
        }

        // Method 2
        System.out.println("Test 2:");
        for (int i = 0; i < 20; ++i) {
            startTime = System.nanoTime();
            testMethod2();
            stopTime = System.nanoTime();
            System.out.println((stopTime - startTime) + "ns");
        }
    }

    public static void testMethod1() {
        // Do nothing
    }

    public static void testMethod2() {
        // Do nothing
    }
}

代码段:Test Snippet

输出如下:

Test 1:
2485ns
505ns
453ns
603ns
362ns
414ns
424ns
488ns
325ns
426ns
618ns
794ns
389ns
686ns
464ns
375ns
354ns
442ns
404ns
450ns
Test 2:
3248ns
700ns
538ns
531ns
351ns
444ns
321ns
424ns
523ns
488ns
487ns
491ns
551ns
497ns
480ns
465ns
477ns
453ns
727ns
504ns

我运行了几次代码,结果大致相同。第一次调用在我的计算机上会更长(> 8000 ns)(Windows 8.1,Oracle Java 8u25)。

显然,第一次调用通常比以下调用慢(某些调用在随机情况下可能会更长)。

更新

我尝试学习一些JMH,并编写测试程序

代码w /样本输出:Code

我不知道它是否是一个合适的基准(如果程序有一些问题,告诉我),但我发现第一次热身迭代花费更多时间(我使用两次热身迭代以防万一特效影响结果)。而且我认为第一次热身应该是第一次打电话并且速度较慢。如果测试合适,就会出现这种现象。

那么为什么会这样呢?

2 个答案:

答案 0 :(得分:0)

你在循环中调用System.nanoTime()。这些调用不是免费的,所以除了空方法所花费的时间之外,你实际上是在测量退出nanime call#1并进入nanime call#2所需的时间。

更糟糕的是,与其他平台相比,你在Windows where nanotime performs worse上这样做了。

关于JMH:在这种情况下,我认为这没有多大帮助。它的设计目的是通过平均多次迭代来测量,以避免死代码消除,考虑JIT预热,避免排序依赖,...而且afaik它也只是在引擎盖下使用纳米级。

它的设计目标几乎与你想要衡量的目标相反。

您正在衡量某事。但是有些东西可能是几次缓存未命中,纳米级调用开销,一些JVM内部(类加载?解释器中的某种懒惰初始化?),......可能是它们的组合。


关键是你的测量不能真正按照面值进行。即使第一次调用方法有一定的成本,你测量的时间也只是提供了一个上限。

答案 1 :(得分:0)

这种行为通常是由编译器或RE引起的。它在第一次迭代后开始优化执行。另外类加载会产生影响(我猜你的示例代码不是这种情况,因为所有类都是在最新的第一个循环中加载的。)

有关类似问题,请参阅this thread

请记住,这种行为通常取决于它所运行的环境/操作系统。