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

前端之家收集整理的这篇文章主要介绍了比起java总和的时候要慢一些前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
情况就是这样
  1. cat sum100000000.cpp && cat sum100000000.java
  2. #include <cstdio>
  3.  
  4. using namespace std;
  5.  
  6. int main(){
  7. long N=1000000000,sum=0;
  8. for( long i=0; i<N; i++ ) sum+= i;
  9. printf("%ld\n",sum);
  10. }
  11.  
  12.  
  13. public class sum100000000 {
  14. public static void main(String[] args) {
  15. long sum=0;
  16. for(long i = 0; i < 1000000000; i++) sum += i;
  17. System.out.println(sum);
  18. }
  19. }

这是结果:

  1. time ./a.out && time java sum100000000
  2. 499999999500000000
  3.  
  4. real 0m2.675s
  5. user 0m2.673s
  6. sys 0m0.002s
  7. Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
  8. 499999999500000000
  9.  
  10. real 0m0.439s
  11. user 0m0.470s
  12. sys 0m0.027s

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

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

  1. clang -v
  2. Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
  3. Target: x86_64-apple-darwin13.4.0
  4. Thread model: posix
  5.  
  6. uname -a
  7. 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编译没有任何特殊的二进制.这并不会改变结果.

  1. gcc sum1b.cpp
  2. clang sum1b.cpp

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

  1. $gcc sum100000000.cpp && time ./a.out
  2. gcc sum100000000.cpp && time ./a.out
  3. 499999999500000000
  4.  
  5. real 0m2.722s
  6. user 0m2.717s
  7. sys 0m0.003s

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

  1. $otool -tV a.out
  2. otool -tV a.out
  3. a.out:
  4. (__TEXT,__text) section
  5. _main:
  6. 0000000100000f50 pushq %rbp
  7. 0000000100000f51 movq %rsp,%rbp
  8. 0000000100000f54 leaq 0x37(%rip),%rdi ## literal pool for: "%ld
  9. "
  10. 0000000100000f5b movabsq $0x6f05b59b5e49b00,%rsi
  11. 0000000100000f65 xorl %eax,%eax
  12. 0000000100000f67 callq 0x100000f70 ## symbol stub for: _printf
  13. 0000000100000f6c xorl %eax,%eax
  14. 0000000100000f6e popq %rbp
  15. 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:

  1. #include <stdio.h>
  2.  
  3. int main(){
  4. long sum=0;
  5. long i;
  6. for( i=0; i<1000000000; i++ ) sum+= i;
  7. printf("%ld\n",sum);
  8. }

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

sum.java:

  1. public class sum {
  2. public static void main(String[] args) {
  3. long sum=0;
  4. for(long i = 0; i < 1000000000; i++) sum += i;
  5. System.out.println(sum);
  6. }
  7. }

结果:

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

  1. $time ./a.out
  2. 499999999500000000
  3.  
  4. real 0m2.436s
  5. user 0m2.429s
  6. sys 0m0.004s

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

  1. $time java sum
  2. 499999999500000000
  3.  
  4. real 0m0.691s
  5. user 0m0.684s
  6. sys 0m0.020s

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

  1. $time java -server -noverify sum
  2. 499999999500000000
  3.  
  4. real 0m0.651s
  5. user 0m0.649s
  6. sys 0m0.016s

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

  1. $time ./a.out
  2. 499999999500000000
  3.  
  4. real 0m0.733s
  5. user 0m0.732s
  6. sys 0m0.000s

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

  1. $time ./a.out
  2. 499999999500000000
  3.  
  4. real 0m0.373s
  5. user 0m0.372s
  6. sys 0m0.000s

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

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

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

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

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

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

猜你在找的Java相关文章