Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to make a Go binary smaller by compiling it with TinyGo?

Tags:

go

tinygo

The TinyGo Homepage says that it is designed for micro controllers (or MCU). I wonder if it is possible to use TinyGo to compile go into small binary for ARM/Linux system?

Currently with UPX --best --lzma, my go code compiles to arm binary about 1MB, I hope to make it even smaller, because the system has limited storage.

like image 443
xrfang Avatar asked Sep 07 '21 08:09

xrfang


People also ask

How do I make the binaries smaller?

At first, use two go build flags, and create your executable like this: go build -ldflags="-s -w" . And then use UPX to make your executable smaller.

Why are Golang binaries so large?

in Go 1.2, a decision was made to pre-expand the line table in the executable file into its final format suitable for direct use at run-time, without an additional decompression step. In other words, the Go team decided to make executable files larger to save up on initialization time.

How big are Golang binaries?

At the start of the study, the size of the Go binary was eleven MB.

What is the size of the go binary?

At the start of the study, the size of the Go binary was eleven MB. The compiled binary contains debug information. In my situation, it is not needed – it is still impossible to debug on target machines due to lack of access. So you can safely remove it by compiling with the necessary flags or using the strip utility.

Is there a weird trick to reduce a go binary size?

Ok, I lied, there's no weird trick. However, you can easily reduce a Go binary size by more than 6 times with some flags and common tools.

How big are the binaries with go tip?

One final note: a lot of work went into the 1.7 cycle to shrink the binaries, including new conditional tree pruning and generic SSA savings. Here's the size of the same binaries built with Go tip, where 100% is the size of the default 1.6.1 build.

How to reduce the size of go-binary?

So we went the way with eleven Mb to 1.9 MB, that is, we have reduced the size of the Go-binary by almost 6 times! Stripping a binary and then packing it with upx is a very effective measure to reduce size. You should not neglect and remove unnecessary dependencies.


Video Answer


1 Answers

tl;dr: basically yes, but you might have to port your program.

I wonder if it is possible to use TinyGo to compile go into small binary for ARM/Linux system?

Yes (reference: https://tinygo.org/docs/guides/linux/)

TinyGo also lets you compile programs for Linux systems, both 32-bit and 64-bit, on both x86 and ARM architectures.

For cross compiling, you can use GOOS and GOARCH as usual.

For example:

GOARCH=arm GOOS=linux tinygo build -o example ./...

As for how smaller the executable will be compared with one with the official go compiler/runtime, it will likely depend on the specifics of your program. It is true though that TinyGo raison d'etre is to provide smaller binaries.


So, we can try building some executables and empirically compare sizes. BTW, I realized that this comparison is available in TinyGo overview page. We can reproduce it here anyway with a few additional details:

First, Go 1.17 is not supported yet (GitHub issue). You have to have Go 1.16 or lower installed in your system to be able to build, or use TinyGo dev branch.

Then, as I mentioned in the comments, keep in mind that TinyGo doesn't support compiling all standard library packages, so if your code makes use of one of them you'll have to port it. This is the list of supported and unsupported packages: https://tinygo.org/docs/reference/lang-support/stdlib/

To run the comparison, I download Go 1.16.5, which TinyGo supports as of today.

# reference: https://golang.org/doc/manage-install
$ go install golang.org/dl/go1.16.5@latest
$ go1.16.5 download

The program:

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello World!")
}

I'm on a mac, so I have to build with the appropriate env vars. I include building with -ldflags="-s -w" as suggested in the comments.

$ go env | grep -E "(GOOS|GOARCH)"
GOARCH="amd64"
GOOS="darwin" 

$ go1.16.5 build -o example_go ./...
$ go1.16.5 build -ldflags="-s -w" -o example_go_ldflags ./...
$ GOROOT="/Users/blackgreen/sdk/go1.16.5" GOARCH=amd64 GOOS=darwin tinygo build -o example_tinygo ./...

$ du -hs example_*
1.9M    example_go
1.5M    example_go_ldflags
64K     example_tinygo

For a simple hello world program, there is a conspicuous gain. Unsurprisingly, it's significantly better than ldflags too.

like image 90
blackgreen Avatar answered Oct 13 '22 20:10

blackgreen