| ▲ | api 17 hours ago |
| Speaking of bloat: why are binaries from Rust or (much worse) Go so damn huge? This is in release mode with debug off. It’s weird because memory use for the same sorts of programs is not much worse than other languages. In Rust memory use seems comparable to C++. In Go there’s a bit more overhead but it’s still smaller than the binary. So all this is not being loaded. I get the sense devs just don’t put a lot of effort into stripping dead code and data since “storage is cheap” but it shows next to C or even C++ programs that are a fraction of the size. I see nothing about Rust’s safety or type system that should result in chonky binaries. All that gets turned into LLVM IR just like C or C++. Go ships a runtime so that explains some, but not all, of its bloat. |
|
| ▲ | AlexeyBrin 17 hours ago | parent | next [-] |
| Mostly because of static linking. C and C++ don't put every library they need in the binary by default. The advantage is that a pure Go or Rust binary just works (most of the time) when copied from one machine to another, you don't have to care about installing other libraries. |
| |
| ▲ | Onavo 17 hours ago | parent | next [-] | | Go especially, on some platforms they go straight to syscalls and bypass libc entirely. They even bring their own network stack. It's the maximalist plan 9 philosophy in action. | | |
| ▲ | self_awareness 15 hours ago | parent [-] | | I don't really like Go as a language, but this decision to skip libc and go directly with syscalls is genius. I wish Rust could do the same. More languages should skip libc. Glibc is the main reason Linux software is binary non-portable between distros (of course not the only reason, but most of the problems come from glibc). | | |
| ▲ | int_19h 5 hours ago | parent | next [-] | | The consequences of this genius decision were stuff like this: https://github.com/golang/go/issues/16570 Which is why they have already backpedalled on this decision on most platforms. Linux is pretty much the only OS where the syscall ABI can be considered stable. | |
| ▲ | badsectoracula 10 hours ago | parent | prev | next [-] | | > Glibc is the main reason Linux software is binary non-portable between distros Linux software is binary portable between distros as long as the binary was compiled using a Glibc version that is either the same or older than the distros you are trying to target. The lack of "portability" is because of symbol versioning so that the library can expose different versions of the same symbol, exactly so that it can preserve backwards compatibility without breaking working programs. And this is not unique to Glibc, other libraries do the same thing too. The solution is to build your software in the minimum version of libraries you are supposed to support. Nowadays with docker you can set it up in a matter of minutes (and automate it with a dockerfile) - e.g. you can use -say- Ubuntu 22 to build your program and it'll work in most modern Linux OSes (or at least glibc wont be the problem if it doesn't). | |
| ▲ | steveklabnik 13 hours ago | parent | prev [-] | | You can only skip libc on Linux. Other unices and Windows don’t let you. | | |
| ▲ | immibis 11 hours ago | parent [-] | | You can skip libc on Windows - you can't skip the system DLLs like kernel32. (In fact, Microsoft provided several mutually incompatible libcs in the past.) Well, you can non-portably skip kernel32, and use ntdll, but then your program won't work in the next Windows version (same as on any platform really - you can include the topmost API layers in your code, but they won't match the layers underneath of the next version). But system DLLs are DLLs, so also don't cause your .exe to get bloated. | | |
| ▲ | steveklabnik 10 hours ago | parent [-] | | Yes, it's not literally libc on windows, but the point is that directly calling syscalls is not supported, you have to call through the platform's library for doing so. On some systems, this is just not a supported configuration (like what you're talking about with Windows) and on some, they go further, and actually try and prevent you from doing so, even in assembly.) | | |
| ▲ | immibis 10 hours ago | parent [-] | | There's still something on the platform that you can call without extra indirection in the way on your side of the handoff. That is true on all platforms; whether it's an INT or SYSCALL instruction or a CALL or JMP instruction is irrelevant. | | |
| ▲ | int_19h 5 hours ago | parent [-] | | If it's a CALL instruction into a user-space DLL, that's still an extra indirection. |
|
|
|
|
|
| |
| ▲ | api 17 hours ago | parent | prev [-] | | That’s a great point. Another advantage is that at least for Rust you can do whole program optimization. The entire program tree is run through the optimizer resulting in all kinds of optimizations that are otherwise impossible. The only other kinds of systems that can optimize this way are higher level JIT runtimes like the JVM and CLR. These can treat all code in the VM as a unit and optimize across everything. | | |
| ▲ | TinkersW 15 hours ago | parent | next [-] | | C++ has had whole program optimization since forever. And you can use static linking if you want, the same as Rust.
| |
| ▲ | aleph_minus_one 16 hours ago | parent | prev [-] | | > Another advantage is that at least for Rust you can do whole program optimization. The entire program tree is run through the optimizer resulting in all kinds of optimizations that are otherwise impossible. I get why this might lead to big intermediate files, but why do the final binaries get so big? | | |
| ▲ | uecker 13 hours ago | parent | next [-] | | Both C++ and Rust are based on monomorphization, which means generic programming is based on a expansion of code for each combination of types. This makes compilation slow and causes code bloat. One then needs whole program optimization to get this under control to some degree. | |
| ▲ | 3836293648 15 hours ago | parent | prev [-] | | Rust binaries + all their dynamic libraries are the same size as C++ binaries + their linked libraries (when stripped, this isn't default in Rust) The main issue is that Rust binaries typically only link to libc whereas C++ binaries link to everthing under the sun, making the actual executable look tiny because that's not where most of the code lives. |
|
|
|
|
| ▲ | Sharlin 17 hours ago | parent | prev | next [-] |
| A good article on minifying Rust binaries: https://github.com/johnthagen/min-sized-rust |
|
| ▲ | throawayonthe 17 hours ago | parent | prev | next [-] |
| https://github.com/johnthagen/min-sized-rust explains some of it
and https://kobzol.github.io/rust/cargo/2024/01/23/making-rust-b... |
|
| ▲ | 17 hours ago | parent | prev | next [-] |
| [deleted] |
|
| ▲ | kranke155 17 hours ago | parent | prev | next [-] |
| What binaries are a good example of this? |
|
| ▲ | maccard 17 hours ago | parent | prev [-] |
| Rust doesn’t strip debug info by default. |
| |
| ▲ | jeroenhd 16 hours ago | parent | next [-] | | The vast amount of debug info does make the problem worse, but it doesn't take long before a moderately complex Rust programs grows to 100MB even after stripping. When I tried to compare Rust programs to their C(++) equivalents by adding the sizes of linked libraries recursively (at least on Linux, that's impossible for Windows), I still found Rust programs to have a rather large footprint. Especially considering Rust still links to glibc which is a significant chunk of any other program as well. I believe many of Rust's statically linked libraries do more than their equivalents in other languages, so I think some more optimisation in stripping unused code paths could significantly reduce the size of some Rust applications. | |
| ▲ | 15 hours ago | parent | prev [-] | | [deleted] |
|