Go 和 D 宣称拥有非常快的编译器。
由于语言本身的现代设计考虑了并发单程解析。
了解大部分构建时间浪费在链接阶段。我想知道为什么 gcc 在小程序上仍然更快。
C
#include <stdio.h>
int main() {
printf("Hello\n");
}
$ time gcc hello.c
real 0m0.724s
user 0m0.030s
sys 0m0.046s
D
Idiomatic
import std.stdio;
void main() {
writeln("Hello\n");
}
$ time dmd hello.d
real 0m1.620s
user 0m0.047s
sys 0m0.015s
With hacks
import core.stdc.stdio;
void main() {
printf("Hello\n");
}
$ time dmd hello.d
real 0m1.593s
user 0m0.061s
sys 0m0.000s
$ time dmd -c hello.d
real 0m1.203s
user 0m0.030s
sys 0m0.031s
Go
package main
import "fmt"
func main() {
fmt.Println("Hello.")
}
$ time go build hello.go
real 0m2.109s
user 0m0.016s
sys 0m0.031s
Java
public class Hello {
public static void main(String[] args) {
System.out.println("Hello.");
}
}
$ time javac Hello.java
real 0m1.500s
user 0m0.031s
sys 0m0.031s
运行 compiler filename
实际上仍然运行链接器并且可能将大量标准库复制到生成的可执行文件中(特别是伤害 D 和 Go,它们默认静态链接它们的语言运行时以获得更好的效果兼容性)。
给定这个微不足道的 D hello world:
import std.stdio;
void main() { writeln("hello world"); }
让我在我的电脑上给你看一些时间:
$ time dmd hello.d
real 0m0.204s
user 0m0.177s
sys 0m0.025s
对比使用-c
跳过链接步骤,意思是“编译,不链接”:
$ time dmd -c hello.d
real 0m0.054s
user 0m0.048s
sys 0m0.006s
将第一次运行的时间减少到大约 1/4 - 在这个小程序中,近 3/4 的编译时间实际上是在链接。
现在,让我稍微修改一下程序:
import core.stdc.stdio;
void main() { printf("hello world\n"); }
$ time dmd -c hello.d
real 0m0.017s
user 0m0.015s
sys 0m0.001s
用 printf 代替 writeln 减半!我会回到这个问题上。
并且,为了比较,编译+链接:
$ time dmd hello.d
real 0m0.099s
user 0m0.083s
sys 0m0.014s
这让我们了解正在发生的事情:
D(我猜是 Go,但我对它们了解不多)旨在减少编译大中型程序的时间。小程序已经很快 - 等待几分之一秒(或者在较慢的计算机上可能等待一两秒,我现在使用的那个有一个很好的 SSD 可以加快速度,在旋转硬盘上运行相同的命令磁盘大约是时间的两倍!)对于小型构建来说没什么大不了的。
不过,为大型构建等待几分钟 是个问题。如果我们可以将时间缩短到几秒钟,那就是一个重大胜利。
编译 100,000 行的时间比编译 10 行的时间更重要。所以初始化时间并不重要。链接时间很重要,但编译器本身并没有做太多这方面的工作(链接器是由不同团队编写的独立程序,尽管其他地方也在努力改进它)。
因此,D 构建标准库所花费的时间令人印象深刻。比 C hello world 慢(因为 C 编译器对较小的 lib 做的工作更少),但是你已经看到了 C++ hello world 的好处,它每行速度更慢,并且在每次构建时往往有更多的工作要做(解析#includes 等)。
一个好的编译器基准测试应该比小程序更能隔离这些问题并测试可伸缩性。尽管如果您正确运行测试以确保公平比较,D 在小程序上也做得非常。