>Combined with the Rust rewrite, ICU changes, and identical code folding, Bun's binary size shrinks by ~20% on Linux & Windows.
People who are surprised by this probably has not seen what Zig code actually looks like. Zig's explicitness and lack of abstraction have a real cost that it is basically one of the most verbose programming languages I've ever seen, it's somehow even more verbose than Go. Basic features of modern languages like pattern matching and generics, and as you can see, having to manually clean up everything means that if you forget once, it's a memory leak. Having SOME abstraction is actually good if it prevents you from making mistakes.
Ironically, Zig is a programming language that's probably best written by LLMs, since they can actually tolerate the verbosity.
Not a compiler expert - shouldn't language verbosity and binary size be, at best, very loosely related?
Fair point, I phrased that too broadly, and you are right about the loose correlation.
What I was gesturing at, badly, was more that Zig’s low-abstraction / explicit-by-default syntax tends to have you write more boilerplate-y code in general that are more annoying to write and maintain, while not buying you enough over a language with better tooling and ecosystem and compiler optimization like Rust.
I don't think you can draw the conclusion that source length and binary size are correlated. For example, in Rust:
Rust's enums can carry data. You can write the same thing in C, but because it does not have the enum feature, you have to do it yourself. They're sometimes called "tagged unions" for a reason, you use a union + a tag when doing it by hand: I haven't actually compiled this, but it should compile to almost the exact same, if not literally the exact same, machine code. Yet one is way more verbose than the other.I'm not sure individual examples is the right way to go about this. A correlation isn't a guarantee for every instance and it's easy to concoct individual examples which tell any story you'd like them to.
To properly answer this you'd need to compare a large number of identical implementations written idiomatically in several languages and see if there is a correlation.
If I were to throw my 2 cents in I'd say "a very weak correlation" is probably right. Not because verbose languages HAVE to result in more bloated code but because it seems to me languages fine having a lot of bloat in the syntax also tend to be languages fine having a lot of bloat in the implementation or attracted to abstraction (which never does seem to actually compile away fully in large projects, even though it often largely does).
Sure, I did not think that one example is a full survey of all possibilities. I think that it's quite intuitive that this feels right:
> it seems to me languages fine having a lot of bloat in the syntax also tend to be languages fine having a lot of bloat in the implementation or attracted to abstraction
Which is why I chose an example of the exact opposite: a language not known for bloat, taking way more code to produce the exact same thing as one that's more succinct.
It's not as good as some sort of scientific survey of a wide variety of options, but if you can find examples in all directions, assuming there's no correlation until proven otherwise is a pretty solid bet, I think.
I'm not saying it's not as good as a scientific survey, I'm saying it results in no information at all about the strength of correlation one way or the other.
E.g. one could seek to find a 6' preteen and 6' adult to construct a counterexample to the idea height is in some way correlated with age. Doing so gives just as little evidence of what the strength of correlation is as seeking to find a 5'9" preteen and a 6' adult to show the correlation is positive or seeking to find a 6'1" preteen and a 6'0 adult to show the opposite. I.e. it doesn't follow one can filter the search as they please and then assert that's what the correlation of the unfiltered searches should be assumed to look like. In all 3 cases of positively correlated, negatively correlated, and not correlated we'd expect to be able to construct an example which says whatever we want to say - that isn't the same thing as sampling what the actual correlation usually is.
I think you are saying the same thing as benced - just because Zig source code is verbose is no reason to assume the binary should be larger.
I read my parent ask asking a question: is there a correlation, or not?
I am saying that I do not believe there is a correlation between source code length and binary length. If that's what benced meant by their question, then yes, I agree :)
I’m quite sure there is a certain amount of correlation unfortunately, mainly because there are micro patterns (e.g. IO, allocator) that can’t be modularized into functions. Lots of manual copy-pasta.
It required a little bit of messing with optimisation settings and library generation in Rust, but they emit very very similar x86-64 assembly:
https://godbolt.org/z/89W4srz4d
Nice, thank you for picking up after my laziness. Surely only a few bytes different in the binary, and much, much smaller of a delta than the source.
You can further reduce the difference by passing Expr by pointer in the C version. At that point I think the only difference in the assembly is the order in which the cases are handed.
Ah yeah, honestly both should probably be passed by pointer anyway. But that makes me wonder about the actual differences here and why... maybe something fun to dig into.
Passing by pointer (in C) reduced the difference a lot, but swapping the order of Add and Int in the Rust enum was enough to reduce the different to:
vs LBB0_3 and LBBO_2 were the same in both outputs (up to alpha renaming).Oddly, both sources seemed to be quite sensitive to match switch and enum reordering, resulting in very different generated code. Possibly something to look into further.
Why? Python is terse but has large binaries because of the runtime overhead. C++ is fairly verbose but can make useful binaries in double digit kib.
> Ironically, Zig is a programming language that's probably best written by LLMs, since they can tolerate actually tolerate the verbosity.
Rust in my opinion feels the same.
I have found LLMs struggle with Rust's constraints - they are optimized to produce code that passes the tests, not necessarily good code. So instead of working out lifetimes and borrowing, it will be happy to copy a buffer many times without thought. This means I have to still go through line by line to review and often rewrite either by hand or with another LLM iteration.
There may be some prompting that can help with this but I suspect there is a fundamental tension between writing working code vs good code in LLMs. Go is popular for being simple, making it easy to jump in and write something fast and stable - minimizing the gap between working and good code probably helps out the LLMs a lot.
> There may be some prompting that can help
Sounds like a good time to tell Claude to create a system wide memory of how to implement lifetimes and borrowing and what to NEVER do.
Claude will then make the memory file. Usually memories help a lot. I see it think about it when it reasons through code.
Agree. But just because it feels the same doesn’t mean it compiles the same.
I don’t feel the verbosity with Rust. Haven’t written it in a while but now in the LLM era I’m looking forward to saying “sort out the lifetime errors for me”.
I trust a lot more Rust code generated by an LLM than anything else ngl.
That can often depend on how you write it.
Zig is indeed verbose in some aspects, but not overall. For example, its `try error-union` syntax eliminates a lot of boilerplate code.
The main reason why Zig is verbose in some aspects is the main goal of Zig is program performance. It is a worthy tradeoff.
Abstraction doesn't necessarily lead to a smaller binary. Much of the bloat in modern software is indeed due to (bad) abstractions.
Naive was 4-9% on the initial pass.
Also note that the larger percentages were against already smaller binaries. That smells like there was a single large constant number that got saved somewhere rather than general improvements.
> After that initial shrinkage, the team explored more opportunities for binary size reduction using linker optimizations like Identical Code Folding, removing unused data from ICU, and lazily decompressing small parts of libicu with a zstd dictionary on-demand.
I'd be VERY interested in seeing what the individual effects of those parts were.
The twenty percent quoted is referring to the size of the compiled artifact (one assumes ELF or Mach-O).
Whether or not a language is verbose or obscure is very much about your coordinate system. Not unlike safety.
I think C is a reasonable zero for both things.
Zig is more succinct and safer than C while still being comparably ergonomic. Rust is (mostly) safer and more succinct than Zig while being dramatically less ergonomic (take it up with Wadler memory chads, no one likes affine types).
I like lean4, which is dramatically safer, more succinct, and more ergonomic than Rust.
But I can see why some would say it's a bit too succinct.
> Rust is (mostly) safer and more succinct than Zig while being dramatically less ergonomic
This is just your opinion.
Well, it's my opinion. But it's also the opinion of the broader functional programming community from 1993 to the present day. This notably includes the quite serious Haskellers who designed Rust for the highly specific and demanding requirements of the Servo rendering engine in ~2010. Being as my two parents in web browser layout optimizations were both filed in 2009 I took considerable interest.
It wasn't until 2014 that Orchard formalized the coeffect discharge calculus via indexed monad that makes a binary ownership semantic irretrievably sunsetted as a degenerate case.
It's my opinion. I'm not concerned about how informed that opinion is.