以下小程序计算我们用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