Compiled files in Go
A slight digression today. I noticed that the compiled size of my “hello, world!” program from yesterday was a whopping 2 megabytes, so I’m going to explore reasons for that along with what happens at compilation time.
kai:~/helloworld/$ ll
total 4200
-rwxr-xr-x 1 kai staff 2.0M 26 Jan 15:12 helloworld*
-rw-r--r-- 1 kai staff 74B 26 Jan 16:43 main.go
So, of course, the first thing I did was to create an “empty” program that does absolutely nothing. Its source code is as follows:
package main
func main() {
}
After running go build
on that, turns out the Mac executable is still 1.1MB. So, the base minimum size for any Go program is over a megabyte. Still, they compile and run extremely quickly, so maybe that’s not a bad thing? We have plenty of bandwidth and memory these days…
But what are the reasons for such relatively large binary files for things that are so simple, or in the case of the above example, do nothing at all? Turns out, unsurprisingly, it has to do with what’s bundled into the executable. The official Go FAQ goes into this in a bit more detail, and compares the build size of a Hello World program in C (750KB or so) and in Go (2MB). The key here is in the concept of static linking. All the packages and required assets are bundled into one thing that gets compiled for a specific computer architecture. That means that my compiled Go program can be sent to anyone with a Mac and it will just run. No requirements to check “oh, do you have X installed?” or “make sure you are running version X of Y”. It just runs. The file size is the price you pay for that.
The bundled information alongside the raw "this is what your program will do" includes some clever debugging and stack trace information, according to the Go FAQ. This is borne out by the analysis done by Raphael 'kena' Poss at Cockroach Labs which shows that the runtime information, as in "this is what your computer needs to know about running this code" is 900K of any given Go program.
If I'm only using one (the Println
) method of the fmt
package, is it worth importing the whole thing? The answer, at least to me today, right now, is "yeah". There is zero incentive for me to start learning inventing the wheel to implement some sort of "print to screen" functionality in order to shave off some Kilobytes. I'm not programming for the Demoscene anyway.
This interesting sidetrack has got me reading about all the Go standard library. There's implementations for everything from zipping and unzipping files, as well as encoding and decoding jpeg files. Personally, I've always found it a bit of a mental challenge to find out "has X already been implemented, and how to find out about it?" as well as remembering all of it. I think a good rule of thumb might be to rubber duck as I go and google "how to do in " pretty much before I do anything new to avoid an embarrassment of "uh, why have you implemented a horrendously inefficient way to do this when there's a perfectly good internal method provided by the standard library?"
References and further reading
- Go Standard Library
- “Why are my Go executable files so large?” on Cockroach Labs
- “Why is my trivial program such a large binary?"
- Cover image: Photo by freestocks on Unsplash
Short one for today, as I have had a headache all day. Hopefully more in-depth tomorrow.
Top comments (0)