This weekend I spent some time thinking that (Golang built) binaries of dpcmder are a bit too huge for my taste. Some would say that ~10 megs is not so much for today's world but ~10 megabytes seemed to me to be too much so I set out on a mission to find out if I can trim dpcmder size a bit.
After some googling, I found this nice "shrink your go binaries" blog which explains exactly what I needed to do to achieve my goal.
It boils down to one change in the current build process (remove debugging info & system table) and one additional step after the build (compress binaries).
Instead of using default go build command, for example:
GOOS=linux GOARCH=amd64 go build -o release/dpcmder-linux-amd64 dpcmder.go
You have to add additional ldflags to instruct the linker to remove all debugging info (-w) & symbol table (-s):
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o release/dpcmder-linux-amd64 dpcmder.go
Actually, this research of ldflags brought me to one other issue on my dpcmder todo list - adding application version for each release. This caused me to use one more ldflag (-X) to change values of version variables:
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X 'github.com/croz-ltd/dpcmder/help.Version=$(git tag | tail -n1)' -X 'github.com/croz-ltd/dpcmder/help.Platform=linux/amd64' -X 'github.com/croz-ltd/dpcmder/help.BuildTime=$(git tag | tail -n1).$(date -u -Iseconds)'" -o release/dpcmder-linux-amd64 dpcmder.go
UPX is a free, portable, extendable, high-performance executable packer for several executable formats.
After installing UPX it is easy to use it and for the best compression results I added the flag "--brute" mentioned in the blog above:
upx --brute release/*
$ upx --brute release/* Ultimate Packer for eXecutables Copyright (C) 1996 - 2013 UPX 3.91 Markus Oberhumer, Laszlo Molnar & John Reiser Sep 30th 2013 File size Ratio Format Name -------------------- ------ ----------- ----------- 7925760 -> 2538980 32.03% linux/elf386 dpcmder-linux-386 9105408 -> 2686192 29.50% linux/ElfAMD dpcmder-linux-amd64 8421068 -> 2772992 32.93% Mach/i386 dpcmder-mac-386 9653400 -> 2785280 28.85% Mach/AMD64 dpcmder-mac-amd64 7698432 -> 2449408 31.82% win32/pe dpcmder-win-386.exe 8865792 -> 2597376 29.30% win64/pe dpcmder-win-amd64.exe -------------------- ------ ----------- ----------- 51669860 -> 15830228 30.64% [ 6 files ] Packed 6 files.
This step took quite a long time to complete but results were worth waiting. As you can see below from binary files update times, some binaries were compressed faster and some were compressed slower (compression of win/386 binaries lasted almost 30 minutes while compression of Linux binaries lasted for around 3 minutes).
$ stat -c "%.19z %n" release/* 2020-01-13 10:23:02 release/dpcmder-linux-386 2020-01-13 10:26:57 release/dpcmder-linux-amd64 2020-01-13 10:29:52 release/dpcmder-mac-386 2020-01-13 10:36:54 release/dpcmder-mac-amd64 2020-01-13 11:03:45 release/dpcmder-win-386.exe 2020-01-13 11:10:54 release/dpcmder-win-amd64.exe
Note that these 6 binaries could be compressed in parallel to achieve much better times on the modern-day multi-core machines.
After the whole process, all 6 binaries are now much smaller. The total size of all binaries shrank from the original 65M to a more acceptable 16M. Sizes for all 6 binaries are given below.
$ ls -sh1 release/ total 65M 11M dpcmder-linux-386 12M dpcmder-linux-amd64 11M dpcmder-mac-386 12M dpcmder-mac-amd64 11M dpcmder-win-386.exe 12M dpcmder-win-amd64.exe
$ ls -sh1 release/ total 50M 7,6M dpcmder-linux-386 8,7M dpcmder-linux-amd64 8,1M dpcmder-mac-386 9,3M dpcmder-mac-amd64 7,4M dpcmder-win-386.exe 8,5M dpcmder-win-amd64.exe
$ ls -sh1 release/ total 16M 2,5M dpcmder-linux-386 2,6M dpcmder-linux-amd64 2,7M dpcmder-mac-386 2,7M dpcmder-mac-amd64 2,4M dpcmder-win-386.exe 2,5M dpcmder-win-amd64.exe