1. 18
  1.  

  2. 7

    Those interested in this might also be interested in the AMA the Go Contributors are doing: https://www.reddit.com/r/golang/comments/46bd5h/ama_we_are_the_go_contributors_ask_us_anything/

    1. 1

      Did they finally solve the bloated binary size issue? It’s basically the only thing keeping me from diving into this language.

      1. 1

        Citation needed?

        1. 1

          He is referring to this ongoing github issue. I do notice consistent growth in binary size on each release (didn’t test with go 1.6 yet).

          Last comment from robpike on that issue:

          Marked this as 1.6. I don’t expect it to be fixed but I want to see us develop an understanding of what’s going on.

          It’s been rescheduled to 1.7 since then.

          1. 1

            Thanks for the links! That reminds me of how the compilation speed is 2x slower too. They need to optimize for 1.7.

        2. 1

          Short answer is they didn’t, but context may help here:

          Go binaries are likely to remain relatively large, compared to C binaries say, because it defaults to static linking. That is, “Hello world” is a little bit of your actual hello-world code and a lot of runtime and stdlib. One piece of good news is it means is the relative effect on big programs is smaller, e.g. Hello World is 2.6MB but Docker unpacked is 30MB.

          I think the static linking tends to be a feature–I can ship a Go binary (if I didn’t add C deps to it) for whatever supported OS without worrying if it’s CentOS 6 and I’m on Ubuntu, or if I’m on Go 1.6 and they have other programs on 1.3, or whatever. It is, anyway, a pattern that the Go community seems to stick with even now that they have alternatives since Go 1.5.

          If you don’t want to use static linking, and you control the whole environment you deploy into, you can go install -buildmode shared std and then go install -linkshared app for a smaller binary that uses a systemwide copy of the stdlib. One of the gophers in the AMA suggests the strip utility as well.

          You can upx amd64 executables, but if you’re mostly thinking about download time, just serving them with gzip is about as good.

          1. 2

            Thank you for your elaboration on static linking, well done! In fact, in our team (suckless.org), we are huge proponents of static linking and are very active in showing the dangers and snake-oil involved with dynamic linking.

            The art of static linking is to write code that can easily be “cut out” by the compiler if not needed. For instance, many libraries are written one-file-per-function, because this allows easy link-time-optimization by the compiler (only include those objects I need). More modern LTO also allows you to do that in “accumulated” code.

            For me, dynamic linking is not a solution here, as this only covers the underlying problem: The Go compiler has to become smarter to see what he is really using. I know this is a difficult issue, but the Go developers fortunately acknowledge it.

            1. 2

              Ahh, that context is really useful (I should’ve clicked your username). I suspect it’ll produce bloated binaries for a long time by suckless standards, even w/team grinding on it.

              In particular, some language features make it tricky for the compiler to prove dead code dead. The worst offender is reflection, which can potentially reach public methods at runtime even when there were no compile-time calls to them. Regular interfaces are also slightly tricky (e.g., almost have to prove a type won’t be Printed to declare its String() string method dead) but better contained. And basic stuff like fmt.Println uses those features. Static analysis can chip away at the problem–I think godoc has some analysis around interfaces now–but it’s definitely a weird place to start from.

              The ssa backend promises somewhat smaller asm output someday, and I can imagine the Go team optimizing formats for debug/reflect-ish data in the binary. Suspect those are going to be percents-at-a-time wins, won’t get us to 100kb Hello World or anything, but don’t know. Anyway, that’s what little I have on prospects for Go binary size. :)

          2. 1

            Just out of curiousity, why is this an issue for you?

            1. 1

              Most developers nowadays write one program to fit many use-cases, but I’m not a big friend of this idea, as I strongly support the Unix philosophy of doing one thing and doing it well. Thus I like writing tools that do one job very well and interlink them with different kinds of IPC (most of the time using UNIX pipes or UDS). For a given problem, separating it into multiple problems can turn out to be very effective (and is actually also a good business model were each employee works on a given sub-problem in a separated binary).

              Now, it’s easy to see why bloated binaries are an issue here. If you have 90+ tools for instance to implement the basic POSIX toolset (cat, ls, od, …), each weighing in around 1MB, you unnecessarily bloat up your bin-folder. And I also don’t want to dynamically link them for obvious reasons.

              Written in C, we could easily fit our toolset on a 1.44MB diskette (check out sbase)

              1. 1

                And I also don’t want to statically link them for obvious reasons.

                What do you mean? I thought you were a proponent of static linking (as a member of suckless.org).

                1. 3

                  Sorry, my mistake. Obviously I meant “dynamically link them”. The reasoning behind this is that in case there is an emergency situation (libc.so is shot or sth, happened to me once), you don’t want to have broken basic tools.

                  If we look at Go binaries, this situation could be even worse and it’s always good to have statically linked basic tools on your hand somewhere, it saved my life back then when my system was shot. However, I don’t want to “bloat” my system with 1MB each fat binaries. If you manage to keep your static binaries slim, they can even be faster than dynamically linked applications due to shared pages and because of the missing dynamic linker’s overhead.

                  One main argument given for dynamic linking is that if there’s a security update for a given library, you just have to recompile the library, but not the program itself. This was a valid concern 10 years ago when it was costy to recompile stuff (nowadays this is only limited to chromium, qt-*, firefox and others, which don’t even use the system-provided libs if you don’t tell them to), however, nowadays, it doesn’t take long to rebuild like 15 tools that depend on a given library. An even stronger point is that symbol versioning effectively destroyed this advantage. Every time there is a library update on Gentoo (even only minor version bumps), it ends up recompiling dozens of programs.

                  A more practical point is the following: As long as the syscall interface is consistent, you can send your compiled static binaries to all your friends without having to worry about dynamic libraries being present on the system (oh, you need libshit.so.6, but you only have libshit.so.5.9 and so on), which is a huge plus and is one reason why the OS X .app’s are so easy to use and “share”. They pack in all the required runtime libraries inside the .app (It’s simply a folder), even though the binary is dynamically linked. On Windows we have the dll-hell, on Linux we have the so-hell. I have witnessed more than one case where users ditched Linux because they couldn’t easily “share” programs with their friends. Even if they had ended up finding the binary they wanted to send somewhere, they would have also had to attach all the dynamic libraries as well. If you have ever built an initramfs by hand, you also know how much of a pain in the ass this is.

                  I am very happy to see that the Go developers have noticed that the cost of dynamic linking is too high. Let’s see what the future will bring.

                  1. 1

                    Sorry, my mistake. Obviously I meant “dynamically link them”.

                    Thanks for the update :-)

                    Your rationale in favor of static linking is really convincing. As a user of Go, I agree that statically linked binaries are a lot easier to deploy.

                    In a world where all binaries would be statically linked, do you think containerization (Docker, LXC, etc.) would still be useful?

                    1. 2

                      This is a very interesting question. First off, we need to analyze that Docker solves multiple problems. Besides containerization, it most importantly is a jail to run sensitive applications in. I am not a big fan of Docker and the entire containerization-aspect will be unnecessary with statically linked applications. Not so the jail part. LXC comes closer to the idea of just having a jail to run your stuff in, so LXC also makes perfect sense with statically linked binaries.

                      All in all though, I would hugely recommend instead using strict PrivSep and PrivDrops in your applications (on OpenBSD using tame() for instance) and running them in a chroot, or a FreeBSD jail on FreeBSD. A chroot only makes sense if you do strict PrivSep and PrivDrop beforehand.

                      If you run applications you don’t have much control of, keep on using FreeBSD jails or LXC. They are fine solutions for the job, but the latter is for my taste a bit too complex for a userspace application. Just as we can see with FreeBSD jails, the best solutions can only be implemented in the Kernel.