我刚刚在一些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秒。这大约快了三个数量级。
那么......为什么循环计数器的类型会产生如此巨大的差异?
答案 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没有展开)