Optimizing the size of Go binaries
I’ve started to play a bit with Go, and a question quickly came to my mind : how can we optimize the size of the binaries ?
Let’s consider the usual “Hello World” :
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello world")
}
And then follow what we can find on the first page of Google when looking for “how to build Go program” (yes, because it’s exactly why we ended-up during years having SQL injections everywhere in PHP scripts, because of people who were just copy/pasting the first Google/SO results without thinking a bit further).
$ go build -o /tmp/test && ls -l /tmp/test
-rwxr-xr-x 1 adedommelin staff 2023776 Mar 21 11:03 /tmp/test
1.9M for a stupid “Hello World” … sounds like we probably have room for improvement !
So, what can we do to reduce the size ? First, let’s remove debug information / DWARF & symbol tables with some linker flags :
$ go build -ldflags "-s -w" -o /tmp/test && ls -l /tmp/test
-rwxr-xr-x 1 adedommelin staff 1524064 Mar 21 11:05 /tmp/test
~25% size reduction with simple linker flags, not too bad !
Let’s try to push a bit further by looking at upx which is a “a free, portable, extendable, high-performance executable packer for several executable formats” (cf: https://upx.github.io/) and run it against our freshly built hello world :
$ upx /tmp/test
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2020
UPX 3.96 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020
File size Ratio Format Name
-------------------- ------ ----------- -----------
1524064 -> 610320 40.05% macho/amd64 test
Packed 1 file.
$ ls -l /tmp/test
-rwxr-xr-x 1 adedommelin staff 610320 Mar 21 11:06 /tmp/test
That’s almost a 70% reduction in size compared to “standard” go build !
Note : there’s for sure a slight overhead due to unpacking at startup which may worth being measured, but for most cases it should definitely not be an issue.