[英]Why does test1() run much faster than test2()?
import java.util.Random;
public class Test{
static int r = new Random().nextInt(2);
static int a(){
return r==1 ? 1 :0;
}
public static void test1() throws Exception {
//
System.out.println(1403187139018L);
for (int i = 0; i < 1073741824; i++) {}//*
// Thread.sleep(20000);
long d = 0;
for (int j = 0; j < 10; j++) {
long y = System.currentTimeMillis();
for (int x = 0; x < 1073741823; x++) {
d += r==0?1:0;
}
System.out.println((System.currentTimeMillis() -y));
}
}
public static void test2() throws Exception{
// Thread.sleep(20000);
long d = 0;
for (int j = 0; j < 10; j++) {
long y = System.currentTimeMillis();
for (int x = 0; x < 1073741824; x++) {
d += r==0?1:0;
}
System.out.println((System.currentTimeMillis() -y));
// System.out.println("time:"+ (System.currentTimeMillis() - y));
}
}
public static void main(String[] args) throws Exception{
// Thread.sleep(20000);
test1();
test2();
}
}
当我运行上面的代码时,我得到以下输出:
32
26
28
28
32
29
35
33
30
31
1321
1308
1324
1277
1348
1321
1337
1413
1287
1331
为什么test1快得多?
除以下内容外没有其他区别:
System.out.println(1403187139018L);
for (int i = 0; i < 1073741824; i++) {}//*
另外,test1的时间成本是25-35毫秒,我认为这是难以置信的。 我用C编写了相同的代码,每个for循环大约需要4秒钟才能运行。
这种行为似乎很奇怪。 我如何知道何时添加:
System.out.println(1403187139018L);
for (int i = 0; i < 1073741824; i++) {}//*
另外,如果我改变
r==0?1:0
至
a()
然后test2()的运行速度比test1()快。
我得到的输出是:
1403187139018
3726
3729
3619
3602
3797
4362
4498
3816
4143
4368
1673
1386
1388
1323
1296
1337
1294
1283
1235
1460
原始旧版代码:...
long t = System.currentTimeMillis();
MappedByteBuffer mbb = map(new File("temp.mmp"), 1024L * 1024 * 1024);
System.out.println("load " + (System.currentTimeMillis() - t));//*
for (int i = 0; i < 2014L * 1024 * 1024; i++) {}//*
int d = 0;
for (int j = 0; j < 10; j++) {
t = System.currentTimeMillis();
mbb.position(0);
mbb.limit(mbb.capacity());
for (int i = 0; i < mbb.capacity(); i++) {
d += mbb.get();
}
....
}
System.out.println(d);
空循环可能会在第一种方法中触发JIT编译。 而且,JIT足够聪明,可以意识到您的代码除了获得当前时间和打印时间差之外,没有做任何有用的事情。 因此,它通过根本不运行来优化无用的代码。
如果您编写实际的,有用的代码,则JIT将做正确的事情。 不要试图通过添加空循环来弄乱它。
影响JIT编译的因素太多:
test1
,统计信息是在第一个(空)循环内收集的,因此使JIT编译器无法真正执行场景。 test1
删除第一个System.out.println
时,并非所有与打印相关的类都被初始化。 尝试调用未初始化类的方法会导致不常见的陷阱,从而导致使用新知识对方法进行优化和进一步重新编译。 r==0?1:0
替换为a()
时,在test1
中收集的错误统计信息会成为一个恶作剧。 在已编译的test1
,方法a()
从未执行过,因此没有机会进行优化。 这就是为什么它比test2
慢的原因,后者已经在a()
的知识下进行a()
编译。 当然,在尝试从头开始编写微基准测试时,很难预测影响JIT编译的所有因素。 这就是为什么推荐的对代码进行基准测试的方法是使用特殊的框架,在这些框架中大多数问题已经得到解决。 我个人建议JMH 。
优化代码后,它将使用有关优化之前代码运行方式的信息。 在test1()中,第一个循环触发整个方法的优化,但是没有关于第二个循环将如何运行的信息,因此它没有像test2()那样被优化
我期望应该发生的是重新优化了该方法,但是代码必须检测到它第一次进行的假设是无效的。
猜测可能有什么不同,test2()可能已经展开循环,而在test1()中则没有。 这可以解释性能上的差异。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.