为什么Java在这里比C运行得更快?

时间:2010-02-14 04:41:10

标签: java c performance

this question的启发,

现在仅对具有>的用户可见10k rep

我想出了以下代码:

$cat loop.c 
int main( int argc, char ** argv ) 
{
    int i = 0;
    while( i++ < 2147483647 );
}

$cc -o loop loop.c  

$ time ./loop
real 0m11.161s
user 0m10.393s
sys 0m0.012s


$cat Loop.java 
class Loop {
    public static void main( String [] args ) { 
        int i = 0;
        while( i++ < 2147483647 );
    }
}

$javac Loop.java 

$time java  Loop  
real 0m4.578s
user 0m3.980s
sys 0m0.048s

为什么Java版本的运行速度比C版本快3倍?我在这里缺少什么?

这是在Ubuntu 9.04上运行的:

Intel(R)Pentium(R)M @ 1.73GHz

32位

修改

这太棒了。在C中使用-O3选项优化循环并在Java中使用-server也是如此。这是“优化时代”。 optimized http://img196.imageshack.us/img196/6489/screenshot4rv.png

9 个答案:

答案 0 :(得分:30)

我希望javac默认为比C编译器更高级别的优化。当我在这里用-O3编译时,C更快:

C -O3

real    0m0.003s
user    0m0.000s
sys     0m0.002s

您的java程序:

real    0m0.294s
user    0m0.269s
sys     0m0.051s

更多细节;没有优化,C编译为:

0000000100000f18 pushq %rbp
0000000100000f19 movq %rsp,%rbp
0000000100000f1c movl %edi,0xec(%rbp)
0000000100000f1f movq %rsi,0xe0(%rbp)
0000000100000f23 movl $0x00000000,0xfc(%rbp)
0000000100000f2a incl 0xfc(%rbp)
0000000100000f2d movl $0x80000000,%eax
0000000100000f32 cmpl %eax,0xfc(%rbp)
0000000100000f35 jne  0x00000f2a
0000000100000f37 movl $0x00000000,%eax
0000000100000f3c leave
0000000100000f3d ret

通过优化(-O3),它看起来像这样:

0000000100000f30 pushq %rbp
0000000100000f31 movq %rsp,%rbp
0000000100000f34 xorl %eax,%eax
0000000100000f36 leave
0000000100000f37 ret

如您所见,整个循环已被删除。 javap -c Loop为java字节码提供了这个输出:

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   iinc    1, 1
   6:   ldc #2; //int 2147483647
   8:   if_icmpge   14
   11:  goto    2
   14:  return

}

看起来循环是编译进来的,我想在运行时会发生一些事情来加速这个循环。 (正如其他人所提到的,JIT编译器会压缩循环。)

答案 1 :(得分:9)

我的猜测是JIT正在优化空循环。

更新:Java性能调优文章Followup to Empty Loop Benchmark似乎支持这一点,以及其他答案,指出C代码也需要进行优化才能进行有意义的比较。关键报价:

  

如果我选择使用客户端模式1.4.1 JVM(客户端是默认模式),则不会优化循环。如果我选择使用Microsoft的C ++编译器,那么C版本就会花费时间。显然,编译器的选择至关重要。

答案 2 :(得分:5)

这里有一些你需要控制的事情:

  • 与启动已编译的C程序相比,JVM的启动是非常重要的
  • 你的循环没有做任何事情,编译器可能知道
  • JIT编译器通常比非优化的C编译器生成更好的代码

答案 3 :(得分:5)

“我在这里缺少什么?”优化标志。

答案 4 :(得分:5)

我不认为这个问题确实有答案;它取决于编译器执行的优化。在这种情况下,我希望,如果进行足够的优化工作,将完全消除循环,因为永远不会使用i

答案 5 :(得分:4)

Optimization - 您至少错过了-O2命令行上的gcc标记。

答案 6 :(得分:2)

Java JIT编译器非常智能,可以优化循环,而C编译器似乎关闭了大部分优化。

所以你真的要比较启动Java机器的时间和未经优化的C代码计算到20亿的时间。

答案 7 :(得分:2)

因为程序没有做任何事情,优化器可以删除循环


如果您正在尝试让编译器执行某个单元或者基准的工作,那么您需要欺骗它以认为实际上将使用该工作的结果。

执行此操作的一种方法是在一个文件中编写函数,编译它,然后使用另一个文件中的安装程序调用它。没有编译器可以预测将来会编译什么。

如果没有它,它只是默认优化级别之间的竞争而没有用处。

答案 8 :(得分:0)

你的程序绝对没有任何内容,所以这对两种语言的性能都没有任何说明。它告诉你的唯一事情是你的编译器是否能够解决这个问题,因此完全跳过你的程序。

要使它做“某事”,你必须将每个增量打印到stdout。如果只打印最终结果,那么一个好的编译器可以将你的程序优化为只打印这个结果并跳过整个“计算”的语句。