长循环计数器似乎非常低效

时间:2014-05-12 17:02:47

标签: java performance loops for-loop long-integer

我刚刚在一些Java玩具示例中发现了一个奇怪的性能问题。这是完整的程序:

import java.util.Random;

public class Weird
{
    public static long fib(int n)
    {
        long a = 0;
        long b = 1;
        for (long i = 0; i < n; ++i)   // note: loop counter of type long
        {
            long c = a + b;
            a = b;
            b = c;
        }
        return a;
    }

    public static void main(String[] args)
    {
        System.out.print("Warming up the JIT... ");
        warmUpTheJIT();
        System.out.println("Starting measurement");

        long bogus = 0L;
        long before = System.nanoTime();
        for (int i = 0; i < 500_000_000; ++i)
        {
            bogus += fib(46);
        }
        long after = System.nanoTime();
        System.out.println(bogus);

        long duration = after - before;
        long ms = duration / 1_000_000;
        System.out.println(ms +" ms");
    }

    private static void warmUpTheJIT()
    {
        Random rand = new Random();
        long bogus = 0L;
        for (int i = 0; i < 1_000_000; ++i)
        {
            bogus += fib(rand.nextInt(47));
        }
        System.out.println(bogus);
    }
}

我的系统(64位Java 8 VM)上的代码需要21秒。但是,如果我将循环计数器从long更改为int,则只需要27 MILLI秒。这大约快了三个数量级。

那么......为什么循环计数器的类型会产生如此巨大的差异?

1 个答案:

答案 0 :(得分:1)

这就是我写它的方式

public class IntLoopMain {
    public static long fib(int n) {
        long a = 0;
        long b = 1;
        for (long i = 0; i < n; ++i) {  // note: loop counter of type long
            long c = a + b;
            a = b;
            b = c;
        }
        return a;
    }

    static volatile long total = 0;

    public static void main(String... args) {
        System.out.println("Warming up the JIT... ");

        long start = 0;
        for (int i = -100000; i < 200_000_000; ++i) {
            if (i == 0) {
                start = System.nanoTime();
                System.out.println("Starting measurement");
            }
            total += fib(46 + (i & 3));
        }
        long time = System.nanoTime() - start;
        if (total == 0)
            System.out.println(total);

        System.out.printf("Took %.3f secs%n", time / 1e9);
    }
}
带有long i循环的

Took 8.166 secs
带有int i循环的

Took 3.549 secs

如果我拿出+ (i & 3)

Took 7.296 secs <- long
Took 1.317 secs <- int

我的观点是int循环的优化程度更高(因为它们更常见)

假设我做了一些手动循环展开。 AFAIK int循环执行更多展开。

public static long fib2(int n) {
    long a = 0;
    long b = 1;
    for (int i = 1; i < n; i+=2) {  // note: loop counter of type long
        long c = a + b;
        long d = b + c;
        a = c;
        b = d;
    }
    return (n & 1) == 0 ? a : b;
}

使用fib2(46)

Took 3.828 secs <- long
Took 1.321 secs <- int

手动展开不会有帮助(除非JIT没有展开)