This is a known disadvantage to the new std.Io I suspect. Basically the way the new IO system is written in the standard library, it’s far harder for the compiler to be sure about which IO functions are not being used. So it’s including a bunch of functions “just in case”. [1]
There are plans afoot to give the compiler more information about what is unused so it can strip that code out again. So, hopefully, this is a temporary regression.
Slightly longer version: As std.Io is a table of function pointers to all IO functions, using std.Io means a pointer exists to every function. Therefore they are “used” and have to be included in the binary, even if those pointers are never dereferenced by your code. ↩︎
I think the difference in size between zig c++ and clang is because zig will link libc++ statically into your binary. Where as clang will dynamically link it to your system’s libc++.
You can confirm this by running ldd against the binaries they produce. For me it looks like this for a c++ hello world:
All that extra code that is linked to the system for the clang build has to be included into the binary itself for the zig build, hence why it’s bigger.
objdump is also a nice utility to investigate binary bloat. It will show a bunch of c++ panic handlers, etc. in the zig binary that are missing from the gcc/clang binary. If std.Io was included in your binary, you would see a bunch of symbols called Io.Threaded.... But they shouldn’t be there for a zig c++ binary.