如何减少编译文件的大小?


82

让我们比较一下c:Hello_world.c:

#include<stdio.h>
int main(){
    printf("Hello world!");
}

Hello_world.go:

package main
import "fmt"
func main(){
    fmt.Printf("Hello world!")
}

都编译:

$gcc Hello_world.c -o Hello_c 
$8g Hello_world.go -o Hello_go.8
$8l Hello_go.8 -o Hello_go

还有,这是什么?

$ls -ls
... 5,4K 2010-10-05 11:09 Hello_c
... 991K 2010-10-05 11:17 Hello_go

大约1Mb的世界。你在跟我开玩笑吗?我做错了什么?

(仅Hello_go-> 893K条)


2
在x86_64 Mac上,“ Hello World”二进制文件的大小为1.3 MB,就像在我假设的x64 Linux机器上一样。相反,ARM x32二进制文件与x86_32二进制文件一样大。大小很大程度上取决于相应体系结构的“字”长度。在x32机器上,它是32位,在x64上是64位宽。因此,x32“ Hello World”二进制文件要小30%。
亚历克斯(Alex)2010年

17
@尼克:考虑到GO是作为一种系统语言销售的,我认为这是一个公平的问题。我在系统中工作,我们并不总是拥有4GB +的RAM和巨大的磁盘。
Ed S.

3
一个893kb的可执行文件与“ 4GB +的RAM和一个巨大的磁盘”相去甚远,而且正如其他人已经指出的那样,这包括静态链接的go运行时,可以很容易地排除它。
尼克·约翰逊

22
是的,这是一个遥不可及的问题,我知道答案,但这是一个有效的问题,“谁在乎内存消耗”的态度往往来自于在无关紧要的系统上工作。您似乎认为这种无知可以,最好不要问问题。我会再说一遍;有时〜1MB很大,您显然不在那个世界上工作编辑-您在Google!上工作,我还是不明白不过,“谁在乎”的态度
Ed S.

12
显然在Java部门;)
Matt Joiner 2010年

Answers:


29

文件较大是否有问题?我不知道Go,但是我认为它会静态链接某些运行时库,而C程序则不是这种情况。但是,只要程序变大,就不必担心。

如上所述这里,静态链接Go运行时是默认的。该页面还告诉您如何设置动态链接。


2
有一个未解决的问题,Go1.5的里程碑已定

80

如果您使用的是基于Unix的系统(例如Linux或Mac OSX),则可以尝试通过使用-w标志构建可执行文件中包含的调试信息来删除它:

go build -ldflags "-w" prog.go

文件大小大大减少。

有关更多详细信息,请访问GDB的页面:http : //golang.org/doc/gdb


3
使用strip命令可以达到相同的效果:go build prog.go; strip prog然后我们得到了所谓的条带可执行文件:ELF 64位LSB可执行文件,x86-64,版本1(SYSV),动态链接(使用共享库),已剥离
Alexander I.Grafov

1
这在Windows上有效,并且与通过正常构建和剥离产生的二进制文件相比,提供的二进制文件略小。
Isxek

9
@Axel:明显不支持剥离go二进制文件,在某些情况下会导致严重的错误。看这里
Flimzy 2014年

9
当前文档列表-w,而不是-s。不知道有什么区别,但是您可能想更新答案:-)
Dynom

1
@Dynom:-w似乎减少了一些大小,但是减少程度不如-s好。-s似乎暗示-w:golang.org/cmd/link
arkod

42

2016年答案:

1.使用Go 1.7

2.编译 go build -ldflags "-s -w"

➜ ls -lh hello
-rwxr-xr-x 1 oneofone oneofone 976K May 26 20:49 hello*

3.然后使用upxgoupx不再因为需要1.6。

➜ ls -lh hello
-rwxr-xr-x 1 oneofone oneofone 367K May 26 20:49 hello*

3
我想在这里补充一点,有关UPX的通常警告可能适用于此。请参阅此以获取更多信息:stackoverflow.com/questions/353634/…(其中大部分也适用于非Windows操作系统)。
乔纳斯(Jonas)2016年

23

Go二进制文件很大,因为它们是静态链接的(使用cgo的库绑定除外)。尝试静态链接C程序,您会发现它增长到可比的大小。

如果这确实对您来说是个问题(我很难相信),则可以使用gccgo进行编译并动态链接。


19
对于一种尚未被普遍采用的语言来说,这是一个不错的举动。没有人会对使用go system libs使系统混乱不堪。
马特·乔纳

4
Go的创建者明确喜欢动态链接:有害.cat-v.org / software / dynamic-linking 。Google静态链接了其所有C和C ++:reddit.com/r/golang/comments/tqudb/…–
Graham King

13
我认为链接的文章提出了相反的建议-Go创建者明确喜欢静态链接。
Petar Donchev 2015年

17

您应该得到goupx,它将“修复” Golang ELF可执行文件以供使用upx。在某些情况下,我已经缩小了约78%的文件大小~16MB >> ~3MB

压缩率通常趋向于25%,因此值得一试:

$ go get github.com/pwaller/goupx
$ go build -o filename
$ goupx filename

>>

2014/12/25 10:10:54 File fixed!

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
  16271132 ->   3647116   22.41%  linux/ElfAMD   filename                            

Packed 1 file.

额外:-s标志(带)可以进一步缩小bin文件goupx -s filename


1
太好了,谢谢!我一直在设置GOARCH = 386,现在只是使用普通upx。
codekoala 2015年

9
从Go 1.6开始不再需要此功能,您可以使用普通的upx。
OneOfOne

12

创建一个名为的文件main.go,让我们尝试使用简单的hello world程序。

package main

import "fmt"

func main(){
    fmt.Println("Hello World!")
}

我使用1.9.1版

$ go version
 go version go1.9.1 linux/amd64

用标准go build命令编译。

$ go build main.go
$ ls -lh
-rwxr-xr-x-x 1 nil nil 1.8M Oct 27 07:47 main

让我们再次使用编译,go buildldflags如上所述

$ go build -ldflags "-s -w" main.go
$ ls -lh
-rwxr-xr-x-x 1 nil nil 1.2M Oct 27 08:15 main

文件大小减少了30%。

现在,让我们使用gccgo

$ go version
 go version go1.8.1 gccgo (GCC) 7.2.0 linux/amd64

建筑一起去gccgo

$ go build main.go
$ ls -lh
-rwxr-xr-x 1 nil nil 34K Oct 27 12:18 main

二进制大小减少了近100%。让我们再一次尝试main.go使用gccgowith和build标志来构建,

$ go build -gccgoflags "-s -w" main.go
-rwxr-xr-x 1 nil nil 23K Oct 27 13:02 main

警告: 由于gccgo二进制文件是动态链接的。如果二进制文件的大小很大,则使用gccgo编译时,二进制文件不会减少100%,但是大小会减少很多。

与gc相比,gccgo编译代码的速度较慢,但​​支持更强大的优化,因此gccgo构建的受CPU约束的程序通常运行速度更快。多年来,在GCC中实现的所有优化都是可用的,包括内联,循环优化,向量化,指令调度等。尽管它并不总是能产生更好的代码,但在某些情况下,使用gccgo编译的程序可以运行30%更快。

预计GCC 7版本将包含Go 1.8用户库的完整实现。与早期版本一样,Go 1.8运行时没有完全合并,但是Go程序应该看不到它。

优点:

  1. 缩小尺寸
  2. 优化。

缺点

  1. 无法使用的最新版本go

您可以在这里这里看到。


谢谢。据我所读,我有一个问题,该问题gogcc不支持ARM处理器,对吗?并且,您能推荐一种可以减少Golang构建文件大小的工具吗?
罗斯塔米

您如何使用gccgo进行编译?
Vitaly Zdanevich

10

更紧凑的hello-world示例:

package main

func main() {
  print("Hello world!")
}

我们跳过了大fmt包装,并明显减少了二进制文件:

  $ go build hello.go
  $ ls -lh hello
  ... 259K ... hello2
  $ strip hello
  $ ls -lh hello
  ... 162K ... hello2

不像C那样紧凑,但是虽然以K而不是M来衡量:)好的,这不是通用的方法,只是显示了一些优化大小的方法:使用strip并尝试使用最小包装。无论如何,Go并不是制造小型二进制文件的语言。


11
该代码利用了内置的print()函数,因此不建议这种做法。问题不是关于如何减少提供的示例程序的代码大小,而是以更通用的方式。
wldsvc

@wldsvc我同意你的说明。虽然不明白为什么print()是显示输出的坏方法?对于简单的脚本或调试输出,它已经足够了。
亚历山大·格拉夫夫

3
请参阅doc.golang.org/ref/spec#Bootstrapping。 为确保完整性起见,已记录了这些功能,但不能保证始终使用该功能。对我来说,这意味着在任何脚本中都不要使用它们,只是出于一次调试目的。
wldsvc 2014年

3

2018答案为下围棋1.11,如啾啾布拉德·菲茨帕特里克(中2018年6月):

截至几分钟前,#golang ELF二进制文件中的DWARF节现在已压缩,因此,即使所有多余的调试内容也已完成,技巧二进制文件现在比Go 1.10小。

https://pbs.twimg.com/media/DfwkBaqUEAAb8-Q.jpg:大

cf. Golang问题11799

压缩我们的调试信息可能会带来可观的,便宜的文件大小。

594eae5号中查看更多

cmd / link:在ELF二进制文件中压缩DWARF节

其中最棘手的部分是二进制布局代码(blk,elfshbits和其他各种东西)在符号和节的文件位置及其虚拟地址之间假定恒定的偏移量。

当然,压缩会破坏此恒定偏移量。
但是我们需要在压缩之前为所有内容分配虚拟地址,以便在压缩之前解决重定位。

结果,压缩需要根据压缩后的大小重新计算DWARF节和符号的“地址”。
幸运的是,这些位于文件的末尾,因此不会干扰其他任何节或符号。(当然,有大量代码假设DWARF段位于最后,那么还有什么地方呢?)

name        old exe-bytes   new exe-bytes   delta
HelloSize      1.60MB ± 0%     1.05MB ± 0%  -34.39%  (p=0.000 n=30+30)
CmdGoSize      16.5MB ± 0%     11.3MB ± 0%  -31.76%  (p=0.000 n=30+30)
[Geo mean]     5.14MB          3.44MB       -33.08%

Rob Pike提到

这仅对使用ELF的计算机有帮助。
二进制文件仍然太大,并且还在增长。

布拉德回答:

至少是这样。将会变得更糟。
止血一次释放。

原因:调试信息,但还要为GC注册映射,以使任何指令成为保存点。


1

默认情况下,二进制文件包含垃圾收集器,管理go例程的调度系统以及所有导入的库。

结果是最小的大小约为1 Mb。


1

从Go 1.8开始,您还可以使用新的插件系统将二进制文件拆分成类似于共享库的文件。对于此发行版,它仅适用于Linux,但将来可能会支持其他平台。

https://tip.golang.org/pkg/plugin/



1

默认情况下,gcc动态链接并静态链接。

但是,如果您静态链接C代码,则可能会得到一个更大的二进制文件。

就我而言:

  • go x64(1.10.3)-生成的二进制文件大小为1214208字节
  • gcc x64(6.2.0)-生成的二进制文件大小为1421312字节

这两个二进制文件都是静态链接的,并且没有debug_info。

go build -ldflags="-s -w" -o test-go test.go
gcc -static -s -o test-c test.c
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.