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

huangapple go评论143阅读模式
英文:

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年的答案:

  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. 然后使用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程序来说不可见。

优点:

  1. 减小的大小
  2. 优化。

缺点:

  1. 编译速度慢
  2. 无法使用最新版本的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:

  1. Reduced size
  2. Optimized.

Cons

  1. Slow
  2. Cannot use the latest version of go.

You can see over here and here.

答案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 如何减小Go编译文件的大小? 好吧,这不是一般的方法,只是展示了一些优化大小的方法:使用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 如何减小Go编译文件的大小? 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更小,即使有所有额外的调试信息。

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

参见Golang问题11799

> 压缩我们的调试信息可能会带来显著的、廉价的文件大小优势。

更多信息请参见提交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.

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

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.

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

huangapple
  • 本文由 发表于 2010年10月5日 15:29:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/3861634.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定