英文:
How to reduce Go compiled file size?
问题
让我们比较C和Go语言:
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 world。你在开玩笑吗?
我做错了什么?
(strip Hello_go -> 893K only)
英文:
Lets compare c and go:
Hello_world.c :
#include<stdio.h>
int main(){
printf("Hello world!");
}
Hello_world.go:
package main
import "fmt"
func main(){
fmt.Printf("Hello world!")
}
Compile both:
$gcc Hello_world.c -o Hello_c
$8g Hello_world.go -o Hello_go.8
$8l Hello_go.8 -o Hello_go
and ... what is it?
$ls -ls
... 5,4K 2010-10-05 11:09 Hello_c
... 991K 2010-10-05 11:17 Hello_go
About 1Mb Hello world. Are you kidding me?
What I do wrong?
(strip Hello_go -> 893K only)
答案1
得分: 111
如果您使用的是基于Unix的系统(例如Linux或Mac OSX),您可以尝试使用-w标志构建可执行文件来删除其中包含的调试信息:
go build -ldflags "-w" prog.go
文件大小将大大减小。
有关更多详细信息,请访问GDB的页面:http://golang.org/doc/gdb
英文:
If you are using a Unix-based system (e.g. Linux or Mac OSX) you could try removing the debugging information included in the executable by building it with the -w flag:
go build -ldflags "-w" prog.go
The file sizes are reduced dramatically.
For more details visit the GDB's page: http://golang.org/doc/gdb
答案2
得分: 61
2016年的答案:
-
使用Go 1.7版本
-
使用
go build -ldflags "-s -w"
进行编译➜ ls -lh hello
-rwxr-xr-x 1 oneofone oneofone 976K May 26 20:49 hello* -
然后使用
upx
,自从1.6版本以后不再需要goupx
。➜ ls -lh hello
-rwxr-xr-x 1 oneofone oneofone 367K May 26 20:49 hello*
英文:
The 2016 answer:
1. Use Go 1.7
2. Compile with go build -ldflags "-s -w"
➜ ls -lh hello
-rwxr-xr-x 1 oneofone oneofone 976K May 26 20:49 hello*
3. Then use upx
, goupx
is no longer needed since 1.6.
➜ ls -lh hello
-rwxr-xr-x 1 oneofone oneofone 367K May 26 20:49 hello*
答案3
得分: 33
请注意,这个答案已经过时了。请参考其他得票较高的答案。我想删除这个帖子,但已经被接受的答案无法删除。
文件变大是个问题吗?我不了解Go,但我会假设它会静态链接一些运行时库,而C程序则不会。但是当你的程序变大时,这可能不是什么问题。
如这里所述,默认情况下,Go运行时会进行静态链接。该页面还告诉你如何设置动态链接。
英文:
Note: This answer is outdated
Please note that this answer is outdated. Please refer to the other higher-voted answers. I would like to delete this post, accepted answers can't be deleted though.
Is it a problem that the file is larger? I don't know Go but I would assume that it statically links some runtime lib which is not the case for the C program. But probably that is nothing to worry about as soon as your program gets larger.
As described here, statically linking the Go runtime is the default. That page also tells you how to set up for dynamic linking.
答案4
得分: 25
Go的二进制文件很大,因为它们是静态链接的(除了使用cgo的库绑定)。尝试静态链接一个C程序,你会发现它的大小会增长到一个可比较的大小。
如果这对你来说真的是个问题(我很难相信),你可以使用gccgo进行编译并进行动态链接。
英文:
Go binaries are large because they are statically linked (except for library bindings using cgo). Try statically linking a C program and you'll see it grow to a comparable size.
If this is really a problem for you (which I have a hard time believing), you can compile with gccgo and dynamically link.
答案5
得分: 21
创建一个名为main.go
的文件,让我们尝试一个简单的Hello World程序。
package main
import "fmt"
func main(){
fmt.Println("Hello World!")
}
我使用的是Go版本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 build
命令进行编译,但是使用上面建议的ldflags
。
$ 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程序。
$ go build main.go
$ ls -lh
-rwxr-xr-x 1 nil nil 34K Oct 27 12:18 main
二进制文件大小减小了近100%。让我们再次尝试使用gccgo
构建main.go
,但是使用构建标志。
$ 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程序来说不可见。
优点:
- 减小的大小
- 优化。
缺点:
- 编译速度慢
- 无法使用最新版本的
go
。
英文:
create a file named main.go
,
let's try with simple hello world program.
package main
import "fmt"
func main(){
fmt.Println("Hello World!")
}
I use go version 1.9.1
$ go version
go version go1.9.1 linux/amd64
Compile with standard go build
command.
$ go build main.go
$ ls -lh
-rwxr-xr-x-x 1 nil nil 1.8M Oct 27 07:47 main
Let's compile once again with go build
but with ldflags
as suggested above,
$ go build -ldflags "-s -w" main.go
$ ls -lh
-rwxr-xr-x-x 1 nil nil 1.2M Oct 27 08:15 main
File size is reduced by 30%.
Now, lets use gccgo
,
$ go version
go version go1.8.1 gccgo (GCC) 7.2.0 linux/amd64
Building go with gccgo
,
$ go build main.go
$ ls -lh
-rwxr-xr-x 1 nil nil 34K Oct 27 12:18 main
Binary size is reduced by almost 100%.
Let's once again try building our main.go
with gccgo
but with build flags,
$ go build -gccgoflags "-s -w" main.go
-rwxr-xr-x 1 nil nil 23K Oct 27 13:02 main
Warning:
As gccgo
binaries were dynamically linked.
If you have a binary which is very big in size, your binary when compiled with gccgo will not be decreased by 100%, but it will be reduced in size by considerable amount.
Compared to gc, gccgo is slower to compile code but supports more powerful optimizations, so a CPU-bound program built by gccgo will usually run faster. All the optimizations implemented in GCC over the years are available, including inlining, loop optimizations, vectorization, instruction scheduling, and more. While it does not always produce better code, in some cases programs compiled with gccgo can run 30% faster.
The GCC 7 releases are expected to include a complete implementation of the Go 1.8 user libraries. As with earlier releases, the Go 1.8 runtime is not fully merged, but that should not be visible to Go programs.
Pros:
- Reduced size
- Optimized.
Cons
- Slow
- Cannot use the latest version of
go
.
答案6
得分: 16
你应该使用goupx,它可以“修复”Golang ELF可执行文件以与upx
一起使用。在某些情况下,我已经成功将文件大小缩小了约78%,从~16MB
到~3MB
。
压缩比通常为25%,所以值得一试:
$ go get github.com/pwaller/goupx
$ go build -o 文件名
$ goupx 文件名
>>
2014/12/25 10:10:54 文件已修复!
文件大小 比率 格式 名称
-------------------- ------ ----------- -----------
16271132 -> 3647116 22.41% linux/ElfAMD 文件名
已打包1个文件。
额外:-s
标志(strip)可以进一步缩小二进制文件的大小,使用goupx -s 文件名
。
英文:
You should get goupx, it will "fix" Golang ELF executables to work with upx
. I've already got around 78% file size shrink in some cases ~16MB >> ~3MB
.
Compression ratio usually tends to 25%, so it's worth a try:
$ 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.
EXTRA: -s
flag (strip) can shrink bin file even more goupx -s filename
答案7
得分: 9
更紧凑的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并不是用来制作微小大小的二进制文件的语言。
英文:
More compact hello-world example:
package main
func main() {
print("Hello world!")
}
We are skiped large fmt
package and noticeably reduced binary:
$ go build hello.go
$ ls -lh hello
... 259K ... hello2
$ strip hello
$ ls -lh hello
... 162K ... hello2
Not so compact as C, but though measured in K not M Ok, it is not general way just shows some ways to optimize a size: use strip and try to use minimum packages. Anyway Go is not language for making tiny sized binaries.
答案8
得分: 3
默认情况下,gcc使用动态链接,而go使用静态链接。
但是,如果你将C代码静态链接,可能会得到一个更大的二进制文件。
在我的情况下:
- go x64 (1.10.3) - 生成的二进制文件大小为1214208字节
- gcc x64 (6.2.0) - 生成的二进制文件大小为1421312字节
这两个二进制文件都是静态链接的,没有调试信息。
go build -ldflags="-s -w" -o test-go test.go
gcc -static -s -o test-c test.c
英文:
By default gcc links dynamically and go - statically.
But if you link you C code statically, you might get a binary with a bigger size.
In my case:
- go x64 (1.10.3) - generated binary with size 1214208 byte
- gcc x64 (6.2.0) - generated binary with size 1421312 byte
both binaries are statically linked and without debug_info.
go build -ldflags="-s -w" -o test-go test.go
gcc -static -s -o test-c test.c
答案9
得分: 2
2018年对于接下来的Go 1.11的回答,由Brad Fitzpatrick在2018年6月中旬推特上发布:
> 刚刚几分钟前,#golang ELF二进制文件中的DWARF部分现在已经被压缩,所以在tip版本中,二进制文件现在比Go 1.10更小,即使有所有额外的调试信息。
> 压缩我们的调试信息可能会带来显著的、廉价的文件大小优势。
更多信息请参见提交594eae5
> ##cmd/link: 压缩ELF二进制文件中的DWARF部分
> 这个最棘手的部分是二进制布局代码(blk、elfshbits和其他各种东西)假设符号和节的文件位置与它们的虚拟地址之间有一个恒定的偏移量。
> 当然,压缩会破坏这个恒定的偏移量。
但是我们需要在压缩之前为所有东西分配虚拟地址,以便在压缩之前解析重定位。
> 结果,压缩需要根据它们的压缩大小重新计算DWARF部分和符号的“地址”。
幸运的是,它们位于文件的末尾,所以这不会干扰任何其他节或符号。(当然,有很多代码假设DWARF段是最后一个,所以又多了一个地方是可以接受的。)
> 名称 旧exe字节 新exe字节 变化
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)
[几何平均] 5.14MB 3.44MB -33.08%
Rob Pike 提到:
> 这只对使用ELF的机器有帮助。
二进制文件仍然太大,而且还在增长。
Brad回答:
> 至少这是一些进展。本来情况可能会更糟。
为一个版本止住了流血。
原因:调试信息,还有GC的寄存器映射,让任何指令都可以成为保存点。
英文:
2018 answer for the next Go 1.11, as tweeted by Brad Fitzpatrick (mid-June 2018):
> As of a few minutes ago, DWARF sections in #golang ELF binaries are now compressed, so at tip binaries are now smaller than Go 1.10, even with all the extra debug stuff at tip.
Cf. Golang issue 11799:
> Compressing our debug info might offer a significant, cheap file size win.
See more in commit 594eae5
> ##cmd/link: compress DWARF sections in ELF binaries
> The trickiest part of this is that the binary layout code (blk, elfshbits, and various other things) assumes a constant offset between symbols' and sections' file locations and their virtual addresses.
> Compression, of course, breaks this constant offset.
But we need to assign virtual addresses to everything before compression in order to
resolve relocations before compression.
> As a result, compression needs to re-compute the "address" of the DWARF sections and symbols based on their compressed size.
Luckily, these are at the end of the file, so this doesn't perturb any other sections or symbols. (And there is, of course, a surprising amount of code that assumes the DWARF segment comes last, so what's one more place?)
> 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 mentions:
> That only helps on machines that use ELF.
Binaries are still too big, and growing.
Brad replied:
> At least it's something. Was about to be much worse.
Stopped the bleeding for one release.
Reasons: Debug info, but also register map for the GC to let any instruction be a savepoint.
答案10
得分: 1
二进制文件默认包含垃圾回收器、管理Go协程的调度系统以及您导入的所有库。
结果是大约1 Mb的最小尺寸。
英文:
The binary contains by default the garbage collector, the schedulding system that manage the go routines, and all the libraries you import.
The result is a minimal size of about 1 Mb.
答案11
得分: 1
从Go 1.8版本开始,您还可以使用新的插件系统将您的二进制文件拆分成类似共享库的形式。对于此版本,它仅在Linux上工作,但其他平台可能会在将来得到支持。
https://tip.golang.org/pkg/plugin/
英文:
From Go 1.8 you can also use the new plugin system to split up your binary into something that resembles shared libraries. For this release it only works on Linux, but other platforms will probably be supported in the future.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论