The premise

Morgana called my attention in #278 to something interesting:

Also while testing
gccgo creates a yay binary of size 1.5MB
(probably because it links dynamically to alpm.so)
go creates a yay binary of size 4.7MB
gccgo has an install size of 149MB and download size of 22MB
go has an install size of 330MB and download size of 67MB

I was also curious what led to such a difference. My suspicion was the static linking of the go runtime was done by default in go but not gcc-go

First I created a binary from the same release yay-gcc for the gccgo and yay-go for the go version. Both were built using go build (using the respective compiler) so they were not stripped (yay-gcc had 2.4MB and yay-go had 7.3MB)

Testing

ldd

Running ldd to check the direct and indirect libs needed, was already revealing. ldd yay-go displayed 31 lines and ldd yay-gcc displayed 34 lines. In both cases /usr/lib/alpm.so.10 was linked.

1
2
3
4
diff
libgo.so.11
libm.so.6
libgcc_s.so.1

Using pkgfile -s on each file these packages belong to core/gcc-libs and core/glibc

readelf

Running readelf -d binary | grep NEEDED will return our direct dependencies

1
2
3
4
yay-go
 0x0000000000000001 (NEEDED)             Shared library: [libalpm.so.10]
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
1
2
3
4
5
6
7
yay-gcc
 0x0000000000000001 (NEEDED)             Shared library: [libalpm.so.10]
 0x0000000000000001 (NEEDED)             Shared library: [libgo.so.11]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [ld-linux-x86-64.so.2]

So we can estimate the size of the go runtime in yay to be 3.2MB when the binaries are stripped and 4.9MB when not stripped.

radare2

As a comparison I decided to analyze using radare2 the amount of function symbols present using: radare2 and exporting symbols grepping FUNC

1
2
3
4
5
$ radare2 yay-go
[0x00405920]> is~FUNC > yay-go.txt
q
$ wc yay-go.txt
6014  42486 459696 yay-go.txt
1
2
3
4
5
$ radare2 yay-gcc
[0x00405920]> is~FUNC > yay-gcc.txt
q
$ wc yay-gcc.txt
1574  11018 132556 yay-gcc.txt

Roughly a 4440 FUNC symbol difference is noticeable between the 2.

Benchmarking

I would have liked to do proper benchmarking of the 2 binaries, unfortunately it was a bit too much work to set up a reproducible environment to run yay -Syu a 100 times, even without taking into account networking.

On a system without any updates available:

1
2
3
4
5
6
$ time ./yay-gcc -Syu
1.04user 0.08system 0:02.35elapsed 47%CPU (0avgtext+0avgdata 73832maxresident)k
0inputs+96outputs (0major+29015minor)pagefaults 0swaps
$ time ./yay-go -Syu
0.76user 0.03system 0:01.77elapsed 45%CPU (0avgtext+0avgdata 36548maxresident)k
0inputs+80outputs (0major+18211minor)pagefaults 0swaps

After reproducing the same test a couple of times yay-go (as predicted) has a better performance than yay-gcc (the go compiler is more optimized than gcc-go according to some sources online, and accessing linked libraries is always slower).

1
2
3
4
5
6
$ time ./yay-gcc -Ps
0.33user 0.02system 0:00.61elapsed 58%CPU (0avgtext+0avgdata 72988maxresident)k
0inputs+0outputs (0major+11321minor)pagefaults 0swaps
$ time ./yay-go -Ps
0.24user 0.02system 0:00.36elapsed 72%CPU (0avgtext+0avgdata 36204maxresident)k
0inputs+0outputs (0major+6969minor)pagefaults 0swaps

Conclusions

So to conclude:

  • The 4.9MB difference is due to the static linking of the go runtime in yay-go.
  • yay-go is still faster than yay-gcc by about a 100% in mediocrely done benchmarking.