为什么调用函数比不调用函数更快?

时间:2014-02-14 06:56:56

标签: java function methods microbenchmark

我尝试了以下代码:

public class Test {
    public static void main(String[] args) {
        int x = 9, y = 9, z = 0;
        long startTime = System.currentTimeMillis();
        // System.out.println("loop one start time = " + startTime);
        for (int i = 0; i < 10000; i++) {
            for (int j = 0; j < 10000; j++) {
                z = x + y;
            }
        }
        System.out.println("loop one use time = " + (System.currentTimeMillis() - startTime) + ",z = " + z);

        startTime = System.currentTimeMillis();
        // System.out.println("loop two start time = " + startTime);
        for (int i = 0; i < 10000; i++) {
            for (int j = 0; j < 10000; j++) {
                z = sum(x, y);
            }
        }
        System.out.println("loop two use time = " + (System.currentTimeMillis() - startTime) + ",z = " + z);

    }

    public static int sum(int x, int y) {
        int t;
        t = x + y;
        return t;
    }
}

控制台的输出是:

loop one use time = 216,z = 18
loop two use time = 70,z = 18.

似乎第二个循环花费的时间少于第一个循环! 我不明白为什么会这样。谢谢你的帮助。


更新: 我交换了两个循环,现在循环一个花费更少的时间!!

loop two use time = 219,z = 18
loop one use time = 69,z = 18

2 个答案:

答案 0 :(得分:1)

Writing a correct micro-benchmark非常耗时且容易出错。我建议只使用已经可用的库进行微基准测试,例如Caliper,专为此而设计。

您的微基准测试存在很多缺陷,会导致不可预测的结果:

  1. 你没有热身。
  2. 您正在对main方法中的两种方法进行基准测试,从而使JIT编译器更难以优化代码。
  3. 代码“z = x + y;”实际上是“z = 9 + 9;”并且在循环期间不会改变,因此循环可以完全优化到简单表达式“z = 18”。
  4. 无论如何,这是使用Caliper完成的相应基准测试的代码:

    @VmOptions("-server")
    public class Test {
    
        @Benchmark
        public int timeSum1(long reps) {
            int dummy = 0; 
            int x = 9, y = 9;
            for (int j = 0; j < reps; j++) {
                dummy = x + y;
            }
            return dummy;
        }
    
        @Benchmark
        public int timeSum2(long reps) {
            int dummy = 0; 
            int x = 9, y = 9;
            for (int j = 0; j < reps; j++) {
                dummy = sum(x, y);
            }
            return dummy;
        }
    
        public static int sum(int x, int y) {
            int t;
            t = x + y;
            return t;
        }
    }
    

    您可以在此处查看此基准测试的结果:

    结果与预期一致:两种方法大致相同,因为它们可以由JIT编译器内联。使用-server运行这两种方法仍然需要大约相同的时间,但优化得更好。

答案 1 :(得分:0)

从阅读评论我得到了这个想法,尝试下面的代码

public class Test {
    public static void main(String[] args) {
        int x = 9, y = 9, z = 0;
        for (int i = 0; i < 10000; i++) {
            for (int j = 0; j < 10000; j++) {
                z = x + y;
                // z = sum(x, y);
            }
        }
        long startTime = System.currentTimeMillis();
        // System.out.println("loop one start time = " + startTime);
        for (int i = 0; i < 10000; i++) {
            for (int j = 0; j < 10000; j++) {
                z = x + y;
            }
        }
        System.out.println("loop one use time = "
                + (System.currentTimeMillis() - startTime) + ",z = " + z);

        startTime = System.currentTimeMillis();
        // System.out.println("loop two start time = " + startTime);
        for (int i = 0; i < 10000; i++) {
            for (int j = 0; j < 10000; j++) {
                z = sum(x, y);
            }
        }
        System.out.println("loop two use time = "
                + (System.currentTimeMillis() - startTime) + ",z = " + z);

    }

    public static int sum(int x, int y) {
        int t;
        t = x + y;
        return t;
    }
}

结果将显示两个循环具有相同的时间,导致JVM已经预热其内部功能并准备好在他的脚趾上服务。 :)

这也意味着你不能直接将某些代码使用的时间与它的算法直接相关,你需要控制相同的环境和参数来控制代码的时间消耗。