为什么使用gccgo构建的二进制文件更小(除其他差异外?)
我一直在试验gc和gccgo,我遇到了一些奇怪的行为 使用我曾经写过的一个测试定理,我得到了以下结果:(为了可读性,我删除了不必要的信息)为什么使用gccgo构建的二进制文件更小(除其他差异外?),go,gccgo,Go,Gccgo,我一直在试验gc和gccgo,我遇到了一些奇怪的行为 使用我曾经写过的一个测试定理,我得到了以下结果:(为了可读性,我删除了不必要的信息) $time go build-compiler gc-o checkprog\u gc checkprog.go(x 3) go构建0.13s用户0.02s系统100%cpu总计0.149 go构建0.13s用户0.01s系统99%cpu 0.148总计 go build 0.14s用户0.03s系统100%cpu总计0.162 -->平均值:0.13秒用户
$time go build-compiler gc-o checkprog\u gc checkprog.go(x 3)
go构建0.13s用户0.02s系统100%cpu总计0.149
go构建0.13s用户0.01s系统99%cpu 0.148总计
go build 0.14s用户0.03s系统100%cpu总计0.162
-->平均值:0.13秒用户0.02秒系统100%cpu总计0.153
$time go build-编译器gccgo-o checkprog\u gccgo checkprog.go(x 3)
go构建0.10s用户0.03s系统96%cpu 0.135总计
go构建0.12s用户0.01s系统96%cpu 0.131总计
go构建0.10s用户0.01s系统92%cpu 0.123总计
-->平均值:0.11s用户0.02s系统95%cpu总计0.130
$strip-s-o checkprog\u gc\u stripped checkprog\u gc
$strip-s-o checkprog\u gccgo\u剥离checkprog\u gccgo
$ls-l
1834504 checkprog_gc*
1336992检查程序气相色谱仪*
35072检查程序(gccgo)*
24192检查程序gccgo*
$time./checkprog\u gc
./checkprog_gc 6.68s用户0.01s系统100%cpu 6.674总计
./checkprog_gc 6.75s用户0.01s系统100%cpu 6.741总计
./checkprog_gc 6.66s用户0.00s系统100%cpu 6.643总计
-->平均:6.70秒用户0.01秒系统100%cpu总计6.686
$time./checkprog\u gccgo
./checkprog_gccgo 10.95s用户0.02s系统100%cpu 10.949总计
./checkprog_gccgo 10.98s用户0.01s系统100%cpu 10.964总计
./checkprog_gccgo 10.94s用户0.01s系统100%cpu 10.929总计
-->平均10.96s用户0.01s系统100%cpu总计10.947
我可以看到以下模式:
gccgo
构建的二进制文件的大小要小得多(剥离无助于改变这种差异)gc
构建的二进制文件执行速度更快gccgo
构建比使用gc
这三种模式的原因是什么?大小不同,因为gc生成静态二进制文件,gccgo链接到libgo。这意味着整个运行时(调度程序、垃圾收集器、映射、通道)的代码不是gccgo创建的最终二进制代码 编译的速度当然有利于gc。GC是在考虑编译速度的情况下构建的。它通常也会生成较少优化的代码,并且需要执行的工作也较少
现在我们来看看为什么gc速度更快。事实上,两者都不总是比另一个快。例如,尝试md5一个文件,GCCGO将快一个数量级。尝试用很多渠道实现一些东西,gc肯定会赢。你不能总是提前知道哪个会成功。GC倾向于具有更高效的并发性,而gccgo倾向于在数学方面更出色。但是,这是您需要逐个测试的。最好使用go test的基准测试系统,而不是时间。有很多不同之处--:
可以生成一个二进制文件,动态链接到gccgo
,这使得输出更小,但意味着要在目标机器上安装相关的库。没有cgo的Go二进制文件没有这个要求libgo
进行了更多的低级优化,因为它可以使用gccgo
的代码生成器和优化器。gccgo编写了一些数据压缩代码,其运行速度明显快于gcc
。同样的优化使编译器速度变慢:它正在做更多的工作gc
支持gccgo
所支持的目标处理器,因此这是使用SPARC、ARMv8(64位)或POWER等体系结构的唯一方法。(Canonical使用它为arm64和ppc64编译Juju服务编排工具。)gcc
和gccgo
都支持ARMv7(32位),但根据bradfitz的谈话gc
不能生成最有效的ARM代码gc
- 只有
进行了某些优化。gc
- 一个大问题是,编译器确定某些变量永远不会“转义”分配给它们的函数,因此可以进行堆栈分配。(因此,令人惊讶的是,
如果返回值没有转义,可能不会进行堆分配。)这减少了垃圾收集需要运行的频率李>new(T)
- 另一个原因是标准库中的
汇编文件仅通过.s
链接,因此默认情况下gc
不使用英特尔硬件CRC32C之类的东西(您必须提供专门用于gccgo的实现)gccgo
- 一个大问题是,编译器确定某些变量永远不会“转义”分配给它们的函数,因此可以进行堆栈分配。(因此,令人惊讶的是,
首先实现新的语言功能,通常比最新的gc
早一两个版本gccgo
libgo
ldd
或readelf
会告诉你(如果你在Linux系统上)。(制作此CW是因为关于gc/gccgo的差异可能有很多话要说,我不是一个真正的专家,我希望了解更多的人能够编辑它。)
$ time go build -compiler gc -o checkprog_gc checkprog.go (x 3)
go build <...> 0.13s user 0.02s system 100% cpu 0.149 total
go build <...> 0.13s user 0.01s system 99% cpu 0.148 total
go build <...> 0.14s user 0.03s system 100% cpu 0.162 total
--> average: 0.13s user 0.02s system 100% cpu 0.153 total
$ time go build -compiler gccgo -o checkprog_gccgo checkprog.go (x 3)
go build <...> 0.10s user 0.03s system 96% cpu 0.135 total
go build <...> 0.12s user 0.01s system 96% cpu 0.131 total
go build <...> 0.10s user 0.01s system 92% cpu 0.123 total
--> average: 0.11s user 0.02s system 95% cpu 0.130 total
$ strip -s -o checkprog_gc_stripped checkprog_gc
$ strip -s -o checkprog_gccgo_stripped checkprog_gccgo
$ ls -l
1834504 checkprog_gc*
1336992 checkprog_gc_stripped*
35072 checkprog_gccgo*
24192 checkprog_gccgo_stripped*
$ time ./checkprog_gc
./checkprog_gc 6.68s user 0.01s system 100% cpu 6.674 total
./checkprog_gc 6.75s user 0.01s system 100% cpu 6.741 total
./checkprog_gc 6.66s user 0.00s system 100% cpu 6.643 total
--> average: 6.70s user 0.01s system 100% cpu 6.686 total
$ time ./checkprog_gccgo
./checkprog_gccgo 10.95s user 0.02s system 100% cpu 10.949 total
./checkprog_gccgo 10.98s user 0.01s system 100% cpu 10.964 total
./checkprog_gccgo 10.94s user 0.01s system 100% cpu 10.929 total
--> average 10.96s user 0.01s system 100% cpu 10.947 total