wrouesnel / p2cli

pongo2 cli - like the j2cli package in python, but compiles to self-contained go executables
GNU General Public License v2.0
87 stars 19 forks source link

Shrink distributed binary sizes #27

Open lrstanley opened 2 years ago

lrstanley commented 2 years ago

Although the binaries are already quite small for p2, thoughts on potentially reducing the size of them even further? The reason I'm looking to do this we maintain a base image used for 10k+ CI/CD related containers, which means the size of containers is very crucial for us, and I think it's also something that's relatively easy to do.

For example:

Add -s -w to your -ldflags, like so:

CGO_ENABLED=0 GOOS=linux go build -a -ldflags "-s -w -extldflags '-static' -X main.Version=testing" -o /p2 ./cmd/p2

This will strip some debugging and symbol information from the binary which isn't required in runtime (and only needed if you were using delve/gdb for example). More info here. This is something that is done for many go cli utilities.

-s -- Omit the symbol table and debug information. [...] -w -- Omit the DWARF symbol table.

2nd, after the above, is optionally use UPX to compress the binary. It would compress the binary, and add a very lightweight wrapper, that handles the decompression during binary initialization. To users it shouldn't be noticeable -- assuming you use something sane like upx --best --lzma <path/to/binary> and don't use --ultra-brute, which has a higher chance of compressing things so much it may impact how the binary actually works. There is a very small overhead for the binary to initialize, but once it's running there is no runtime-performance issues. On my small VM, it took the initialization from about 10ms to about 57ms . UPX can also cross-compress (i.e. the linux UPX can compress an .exe).

Using golang:1.17, and adding the ldflags, brings it from 8.3M to about 5.2M. Then, installing upx and running upx --best --lzma /p2 brings it down to 2M:

root@c96cfd61c60d:/src# ls -lah /p2*
-rwxr-xr-x 1 root root 2.0M Feb  6 09:08 /p2
-rwxr-xr-x 1 root root 5.2M Feb  6 09:08 /p2.pre-upx
-rwxr-xr-x 1 root root 8.3M Feb  6 09:07 /p2.pre-ldflags

Which means it would shrink the binary down to 24% of its original size.

UPX initialization notes from above (I think this would only be a small time increase even for someone who runs p2 thousands of times in a row):

$ time ./p2.pre-upx
p2.pre-upx: error: required flag --template not provided, try --help

real    0m0.010s
$ time ./p2.with-upx
p2.with-upx: error: required flag --template not provided, try --help

real    0m0.057s
[...]
lrstanley commented 2 years ago

Additionally, thoughts on building and pushing a docker image to Github's container registry (ghcr.io), using Actions when you're pushing a release?

wrouesnel commented 2 years ago

This is a good idea. I'll look into it.