比起java总和的时候要慢一些

前端之家收集整理的这篇文章主要介绍了比起java总和的时候要慢一些前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
情况就是这样
cat sum100000000.cpp && cat sum100000000.java 
#include <cstdio>

using namespace std;

int main(){
  long N=1000000000,sum=0;
  for( long i=0; i<N; i++ ) sum+= i;
  printf("%ld\n",sum);
}


public class sum100000000 {
    public static void main(String[] args) {
        long sum=0;
        for(long i = 0; i < 1000000000; i++) sum += i;
        System.out.println(sum);
    }
}

这是结果:

time ./a.out && time java sum100000000
499999999500000000

real    0m2.675s
user    0m2.673s
sys 0m0.002s
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
499999999500000000

real    0m0.439s
user    0m0.470s
sys 0m0.027s

在拆卸的二进制文件中看不到任何异常的东西.但似乎c二进制显着慢.我不明白

我的猜测是工具链可能有一些问题

clang -v
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix

uname -a
Darwin MacBook-Pro.local 13.4.0 Darwin Kernel Version 13.4.0: Sun Aug 17 19:50:11 PDT 2014; root:xnu-2422.115.4~1/RELEASE_X86_64 x86_64

添加:c / cpp编译没有任何特殊的二进制.这并不会改变结果.

gcc sum1b.cpp
clang sum1b.cpp

添加:对于那些关心llvm,真的没有改变任何东西

$gcc sum100000000.cpp && time ./a.out
gcc sum100000000.cpp && time ./a.out
499999999500000000

real    0m2.722s
user    0m2.717s
sys 0m0.003s

修改:O2更快:但是看起来像一个骗子

$otool -tV a.out
otool -tV a.out
a.out:
(__TEXT,__text) section
_main:
0000000100000f50    pushq   %rbp
0000000100000f51    movq    %rsp,%rbp
0000000100000f54    leaq    0x37(%rip),%rdi ## literal pool for: "%ld
"
0000000100000f5b    movabsq $0x6f05b59b5e49b00,%rsi
0000000100000f65    xorl    %eax,%eax
0000000100000f67    callq   0x100000f70 ## symbol stub for: _printf
0000000100000f6c    xorl    %eax,%eax
0000000100000f6e    popq    %rbp
0000000100000f6f    ret

我现在相信这与Optimization有关,所以现在更多关于JIT做什么来加快这个计算的问题?

解决方法

这个问题很可能是您没有在启用优化的情况下编译C版本.如果您启用了积极的优化,则由gcc生成的二进制文件将获胜. JVM的JIT是好的,但简单的事实是JVM必须在运行时加载并应用JIT; gcc可以在编译时对二进制进行优化.

离开所有gcc标志给我一个二进制,执行得相当慢,就像你的.使用-O2给了我一个几乎不会输出到Java版本的二进制文件.使用-O3给我一个可以轻松打败Java版本的. (这是在我的Linux Mint 16 64位机器上,具有gcc 4.8.1和Java 1.8.0_20 [例如Java 8 Update 20]).larsmans检查了-O3版本的反汇编,并确保编译器不是预先计算结果(我的C和汇编fu在这些天非常弱;非常感谢larsmans双重检查).有趣的是,尽管如此,由于Mat的调查,这实际上是我使用gcc 4.8.1的副产品;较早和更高版本的gcc似乎愿意预先计算结果.对我们而言,快乐的事故

这是我的纯C版本[我还更新了它,以说明你在Java版本中使用常量的Ajay’s comment,但C版本中的变量N(没有任何真正的区别,但…)]:

sum.c:

#include <stdio.h>

int main(){
  long sum=0;
  long i;
  for( i=0; i<1000000000; i++ ) sum+= i;
  printf("%ld\n",sum);
}

我的Java版本与你不同,我不太容易失去零的轨迹:

sum.java:

public class sum {
    public static void main(String[] args) {
        long sum=0;
        for(long i = 0; i < 1000000000; i++) sum += i;
        System.out.println(sum);
    }
}

结果:

C二进制运行(通过gcc sum.c编译):

$time ./a.out
499999999500000000

real    0m2.436s
user    0m2.429s
sys 0m0.004s

Java运行(没有特殊的标志编译,运行没有特殊的运行时标志):

$time java sum
499999999500000000

real    0m0.691s
user    0m0.684s
sys 0m0.020s

Java运行(编译没有特殊的标志,运行-server -noverify,微小的改进):

$time java -server -noverify sum
499999999500000000

real    0m0.651s
user    0m0.649s
sys 0m0.016s

C二进制运行(通过gcc -O2 sum.c编译):

$time ./a.out
499999999500000000

real    0m0.733s
user    0m0.732s
sys 0m0.000s

C二进制运行(通过gcc -O3 sum.c编译):

$time ./a.out
499999999500000000

real    0m0.373s
user    0m0.372s
sys 0m0.000s

这是我的-O3版本的objdump -d a.out的主要结果:

0000000000400470 :
  400470:   66 0f 6f 1d 08 02 00    movdqa 0x208(%rip),%xmm3        # 400680 
  400477:   00 
  400478:   31 c0                   xor    %eax,%eax
  40047a:   66 0f ef c9             pxor   %xmm1,%xmm1
  40047e:   66 0f 6f 05 ea 01 00    movdqa 0x1ea(%rip),%xmm0        # 400670 
  400485:   00 
  400486:   eb 0c                   jmp    400494 
  400488:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40048f:   00 
  400490:   66 0f 6f c2             movdqa %xmm2,%xmm0
  400494:   66 0f 6f d0             movdqa %xmm0,%xmm2
  400498:   83 c0 01                add    $0x1,%eax
  40049b:   66 0f d4 c8             paddq  %xmm0,%xmm1
  40049f:   3d 00 65 cd 1d          cmp    $0x1dcd6500,%eax
  4004a4:   66 0f d4 d3             paddq  %xmm3,%xmm2
  4004a8:   75 e6                   jne    400490 
  4004aa:   66 0f 6f e1             movdqa %xmm1,%xmm4
  4004ae:   be 64 06 40 00          mov    $0x400664,%esi
  4004b3:   bf 01 00 00 00          mov    $0x1,%edi
  4004b8:   31 c0                   xor    %eax,%eax
  4004ba:   66 0f 73 dc 08          psrldq $0x8,%xmm4
  4004bf:   66 0f d4 cc             paddq  %xmm4,%xmm1
  4004c3:   66 0f 7f 4c 24 e8       movdqa %xmm1,-0x18(%rsp)
  4004c9:   48 8b 54 24 e8          mov    -0x18(%rsp),%rdx
  4004ce:   e9 8d ff ff ff          jmpq   400460 

正如我所说,我的汇编很弱,但我看到一个循环,而不是编译器完成了数学.

而只是为了完整,javap -c的结果的主要部分总和:

  public static void main(java.lang.String[]);
    Code:
       0: lconst_0
       1: lstore_1
       2: lconst_0
       3: lstore_3
       4: lload_3
       5: ldc2_w        #2                  // long 1000000000l
       8: lcmp
       9: ifge          23
      12: lload_1
      13: lload_3
      14: ladd
      15: lstore_1
      16: lload_3
      17: lconst_1
      18: ladd
      19: lstore_3
      20: goto          4
      23: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      26: lload_1
      27: invokevirtual #5                  // Method java/io/PrintStream.println:(J)V
      30: return

它不是在字节码级别预先计算结果;我不能说JIT正在做什么.

猜你在找的Java相关文章