C vs Java速度(带有算术的循环)



以下小程序计算我们用C 和Java编写的所有数字的总和,就像我能写的那样。我的理解是,C 是"更快"的语言,但是该代码的Java版本在〜.5秒内完成了C 的〜3秒。

C (GCC编译器):

int main(){
    long long x = 0;
    for (long i=0;i<1000000001;i++){
    x=x+i;
    }
    cout << x << endl;
    return 0;
}

java:

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

如何优化C 代码与Java版本一样快?甚至可能吗?

这个问题是不做什么的完美示例。整个循环等同于单个分配,任何优化编译器都知道。因此,您正在测量启动程序并输出线路需要多长时间。

然后,Java必须以您希望的任何因素输掉,因为运行Java代码包括启动JVM,这非常慢。此外,它包括优化汇编。Javac所做的只是从Java源到Java字节码的汇编,并且没有尝试优化任何内容。所有优化发生在运行时(计算机代码的字节码)。 1

因此,我们可以得出结论,Java对于少于几秒钟的任何任务都非常慢。如果您尝试足够努力,则可以获得20倍或无穷大(零)。

更重要的结论是它没有意义。请参阅如何在Java中编写正确的微基准?如果您想要有意义的结果。


1 这适用于桌面Java。在Android上,它不同。

如果您对优化进行编译,则C 版本的快速 的速度要快。

java:

javac Main.java
$ time java Main
500000000500000000
real    0m0.727s
user    0m0.724s
sys     0m0.004s

c :

clang -O3 main.cpp -o cpp
$ time ./cpp 
500000000500000000
real    0m0.003s
user    0m0.000s
sys     0m0.000s

我的叮当声:

$ clang --version
clang version 4.0.0-1ubuntu1 (tags/RELEASE_400/rc1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

我的Java版本:

$ javac -version
javac 1.8.0_144

原因是优化是一个缓慢的过程。如果关闭优化,您将获得更快的汇编时间。这对开发更适合,因此这是Clang开发人员选择的默认值。Java可能会更快,因为它在运行时进行了更多优化。JVM字节码不是与它收集的源代码不同的

-O选项编译C代码。

没有-o的组装包含大量内存访问(慢):

main:
  push rbp
  mov rbp, rsp
  mov QWORD PTR [rbp-8], 0
  mov QWORD PTR [rbp-16], 0
.L3:
  cmp QWORD PTR [rbp-16], 1000000000
  jg .L2
  mov rax, QWORD PTR [rbp-16]
  add QWORD PTR [rbp-8], rax
  add QWORD PTR [rbp-16], 1
  jmp .L3
.L2:

由-O生成的组装仅使用寄存器:

main:
  mov eax, 1000000001
.L2:
  sub rax, 1
  jne .L2

请参阅Godbolt的GCC Explorer输出:https://godbolt.org/g/rx1va4

编辑:在优化的模式下,编译器认识到输出是常数,这就是为什么没有添加指令的原因。请参阅Nathan的示例,其中输出:https://godbolt.org/g/r1pxvl

最新更新