Golang 使用 upx 减少可执行文件的大小

398 Visits / 0 Comments / Favorite

众所周知,Golang 的编译速度是非常之快的。在设计 Go 时,编译速度是一个重要的考虑因素。但是,你是否关注过 Go 编译代码后生成的二进制可执行文件的大小?让我们来看一个简单的 HTTP 服务的应用示例:

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。

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

现在,让我们尝试优化上面应用的大小。

删除调试信息

默认情况下,Go 编译器会在编译后的程序中包含符号表和调试信息。通常情况下,你可以在发布版本中移除这些调试信息,以减小二进制文件的大小。

➜  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^[1]^ 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.

UPX 是一种先进的可执行文件压缩器。UPX 通常能将程序和 DLL 文件的大小减少 50%-70%,从而减少磁盘空间、网络加载时间、下载时间以及其他分发和存储成本。

在 Mac 上,你可以通过 brew 安装 UPX。

brew install upx

仅使用 UPX 压缩

UPX 有很多参数,其中最重要的是压缩率,范围从 1 到 13。让我们看看仅使用 UPX 就能将二进制文件大小缩小多少。

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

6.5M-> 3.9M 压缩比约为 60%。

UPX + 编译器选项

同时启用 UPX 和 -ldflags="-s -w":

➜  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% 的空间。这对于大型应用程序来说是相当可观的。

How UPX Works

压缩后的程序与原始程序一样,无需解压缩即可正常运行。这种压缩方法称为外壳压缩,包括两个部分:

  • 在程序开头或其他适当位置插入解压缩代码。
  • 压缩程序的其余部分。

执行时还包括两个部分:

  • 开始时插入的解压缩代码首先执行,将原始程序解压缩到内存中。
  • 然后,执行解压缩后的程序。UPX 在执行程序时引入了一个额外的解压缩步骤,但这一时间几乎可以忽略不计。

如果对编译大小没有严格要求,可以选择不使用 UPX 压缩。一般来说,服务器端独立后台服务不需要压缩大小。不过,如果是在 Raspberry Pi 等嵌入式设备上,还是值得一试。

结论

这个帖子有很多很有意思的答案:

  • 例如,在用 C 和 Go 实现相同功能时,C 生成的可执行文件大小是 Go 的 1/20(为什么?)
  • 使用 println 代替 fmt.Println,可以避免导入 fmt 软件包,从而进一步减小程序的大小。

参考资料

[1]UPX: https://upx.github.io/

[2]how-to-reduce-go-compileed-file-size: https://stackoverflow.com/questions/3861634/how-to-reduce-go-compiled-file-size

All comments

Top