Featured image of post Go 高性能编程EP2:  通过upx 缩小可执行二进制文件的体积

Go 高性能编程EP2: 通过upx 缩小可执行二进制文件的体积

 

我们都知道,Go有一个很重要的特点,那就是它的编译速度非常快,编译速度是Go语言设计的时候就重点考虑的问题. 但是您有没有观察过Go语言编译后的二进制可执行文件的大小?我们先用一个简单的http server 的例子来看看。

This article was first published in the Medium MPP plan. If you are a Medium user, please follow me on Medium. Thank you very much.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import (  
    "fmt"  
    "net/http"
    )  
func main() {  
    // create a http server and create a handler hello, return hello world  
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {  
       fmt.Fprintf(w, "Hello, World\n")  
    })  
    // listen to port 8080  
    err := http.ListenAndServe(":8080", nil)  
    if err != nil {  
       return  
    }  
}
---

编译后的体积达到了6.5M

1
2
3
➜  binary-size git:(main) ✗ go build  -o server main.go                 
➜  binary-size git:(main) ✗ ls -lh server 
-rwxr-xr-x  1 hxzhouh  staff   6.5M Jul  2 14:20 server

Go语言的编译器会对二进制文件的大小进行裁剪,如果您对这部分的内容感兴趣,请阅读我的另外一篇文章How Does the Go Compiler Reduce Binary File Size?

现在我们来尝试优化一下server 的大小。

消除调试信息

Go 编译器默认编译出来的程序会带有符号表和调试信息,一般来说 release 版本可以去除调试信息以减小二进制体积。

1
2
3
➜  binary-size git:(main) ✗ go build -ldflags="-s -w" -o server main.go
➜  binary-size git:(main) ✗ ls -lh server                              
-rwxr-xr-x  1 hxzhouh  staff   4.5M Jul  2 14:30 server
  • -s:忽略符号表和调试信息。
  • -w:忽略DWARFv3调试信息,使用该选项后将无法使用gdb进行调试。
    体积从 6.5M 下降到 4.5M,下降约 30%。这是很好的第一步。

使用 upx

UPX is an advanced executable file compressor. UPX will typically reduce the file size of programs and DLLs by around 50%-70%, thus reducing disk space, network load times, download times and other distribution and storage costs.

在Mac 上可以通过brew 安装upx

1
brew install upx

单独使用upx 压缩

upx 有很多参数,最重要的则是压缩率,1-91 代表最低压缩率,9 代表最高压缩率。
接下来,我们看一下,如果只使用 upx 压缩,二进制的体积可以减小多少呢。

1
2
➜  binary-size git:(main) ✗ go build -o server main.go && upx -9 server && ls -lh server 
-rwxr-xr-x  1 hxzhouh  staff   3.9M Jul  2 14:38 server

压缩比例达到了 60%

upx + 编译器选项

同时开启 upx + -ldflags="-s -w"

1
2
➜  binary-size git:(main) ✗ go build -ldflags="-s -w"  -o server main.go && upx --brute server && ls -lh server 
-rwxr-xr-x  1 hxzhouh  staff   1.4M Jul  2 14:40 server

最终我们得到的的可执行文件的大小是 1.4M 对比不开启任何压缩的6.5M,大约节约了80%的空间,对于大型应用,还是挺可观的。

upx 的原理

upx 压缩后的程序和压缩前的程序一样,无需解压仍然能够正常地运行,这种压缩方法称之为带壳压缩,压缩包含两个部分:

  • 在程序开头或其他合适的地方插入解压代码;
  • 将程序的其他部分压缩。

执行时,也包含两个部分:

  • 首先执行的是程序开头的插入的解压代码,将原来的程序在内存中解压出来;
  • 再执行解压后的程序。
    也就是说,upx 在程序执行时,会有额外的解压动作,不过这个耗时几乎可以忽略。
    如果对编译后的体积没什么要求的情况下,可以不使用 upx 来压缩。一般在服务器端独立运行的后台服务,无需压缩体积。

最后

https://stackoverflow.com/questions/3861634/how-to-reduce-go-compiled-file-size
这帖子里面有很多有意思的答案,

  • 比如用c语言跟go语言实现相同的功能(小demo)c语言生成的可执行大小是 go 语言的1/20(为什么呢?)
  • 在Go语言中我们使用 println 来替代 fmt.println 就能避免引入fmt包,进一步缩小体积。
true
最后更新于 Jul 02, 2024 15:01 CST
使用 Hugo 构建
主题 StackJimmy 设计