Live data from Hacker News

Making Rust binaries smaller by default

kobzol.github.io

61–70 of 199 posts

Re: Making Rust binaries smaller by default

#61
post #31

It'd be nice if the default was external symbols rather than none at all

I'd find that a bit counterintuitive. To me, --release is an alternative for --debug. Debug implies debug stuff is in there, therefore the alternative removes it.

But I do find it useful now and then to have debug symbols in production, so that monitoring and telemetry gets some context. But to me, it's rather logical that I need to add this to the build.

I would, however, love it when it's trivial to get these symbols set up external.

Re: Making Rust binaries smaller by default

#62
post #48

Earlier quoted context omitted.

32Kb with the following configuration, on a linux target. It's not the default, you're using nightly, it's more complex to build and there are tradeoffs. You can even go less than that if that's your thing [profile.release] strip = true opt-level = "z" lto = true codegen-units = 1 panic = "abort" cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linu…

These look like sensible defaults to me for release mode. What are the tradeoffs?

opt-level z would not be a reasonable default: you should normally optimise for speed, not binary size.

lto and codegen-units=1 have a huge compile time cost. For release for general distribution you should tend to favour them, but the release profile isn’t just about that activity (especially because debug/opt-levelAbort on panic changes runtime behaviour by stopping you from catching panics, which will completely break some programs, and harm the failure mode of others, so that e.g. one defective route on a web server will suddenly take the entire website down for everyone (or, if you have a supervisor that can restart the server, at least disrupt it for everyone).

Re: Making Rust binaries smaller by default

#63

Earlier quoted context omitted.

You can use the C ABI if you want something stable, and there are custom crates that will help with translating from the Rust to the C ABI. (This also helps because the resulting shared objects can potentially be FFI'd with from any language, not just Rust. They might as well be plain C libraries as far as anyone is concerned, only with the usual Rust safety requirements.)

Also, even in "the" C ABI provided by Rust out of the box there's a certain amount of "Well, this is probably what your C compiler does here, but there's no requirement" rather than an actual hard ABI document. A lot of the "We're ABI stable" claims in C and C++ are "We daren't change anything or stuff breaks" which isn't so much an ABI as it is paralysis and Rust is confident it doesn't want to do that .

> A lot of the "We're ABI stable" claims in C and C++ are "We daren't change anything or stuff breaks" which isn't so much an ABI as it is paralysis

For the C++ standary library maybe, but for pretty much all others which provide ABI compatibility it's a concious and properly followed decision.

Re: Making Rust binaries smaller by default

#64
post #58

Earlier quoted context omitted.

These look like sensible defaults to me for release mode. What are the tradeoffs?

opt-level = "z" may be slightly slower than O2/O3. lto = true and lto-units = 1 makes the final linking unbearably slow for large programs. These are still okay, but then `panic = "abort"` shakes a lot of code off by making the final executable unable to print a nice stack trace (even without symbols) and instead immediately traps/abort()-s. Edit: and GP also used "panic-immediate-abort", which also removes the depen…

Additionally the panic = "abort" disables stack unwinding, and running destructors when panic occurs, so it's a big change to the semantics of the program.

It's comparable to C++ land -fno-exceptions (not exactly, but similar).

Re: Making Rust binaries smaller by default

#65

Earlier quoted context omitted.

I would hope that it could statically link libc and exclude the unreachable portions.

Rust can't do this very well because it doesn't build std as part of the project, that's still an unstable option. It's linking object code.

C doesn't either, but statically linking still pulls only the object files which are actually needed into the executable.

Re: Making Rust binaries smaller by default

#66
post #64
post #58

Earlier quoted context omitted.

opt-level = "z" may be slightly slower than O2/O3. lto = true and lto-units = 1 makes the final linking unbearably slow for large programs. These are still okay, but then `panic = "abort"` shakes a lot of code off by making the final executable unable to print a nice stack trace (even without symbols) and instead immediately traps/abort()-s. Edit: and GP also used "panic-immediate-abort", which also removes the depen…

Additionally the panic = "abort" disables stack unwinding, and running destructors when panic occurs, so it's a big change to the semantics of the program. It's comparable to C++ land -fno-exceptions (not exactly, but similar).

Abort panic strategy is common in firmware and osdev, for anyone wondering if there are other use cases.

Re: Making Rust binaries smaller by default

#67

Earlier quoted context omitted.

These look like sensible defaults to me for release mode. What are the tradeoffs?

opt-level z would not be a reasonable default: you should normally optimise for speed, not binary size. lto and codegen-units=1 have a huge compile time cost. For release for general distribution you should tend to favour them, but the release profile isn’t just about that activity (especially because debug/opt-level Abort on panic changes runtime behaviour by stopping you from catching panics, which will completely…

> you should normally optimise for speed, not binary size

IME optimize for speed vs size usually isn't as clear cut as the name says though, sometimes smaller code does indeed run faster, but in most cases I've seen there's not much of a difference between -O3, -O2, -Os and -Oz.

Re: Making Rust binaries smaller by default

#68
post #48

Earlier quoted context omitted.

32Kb with the following configuration, on a linux target. It's not the default, you're using nightly, it's more complex to build and there are tradeoffs. You can even go less than that if that's your thing [profile.release] strip = true opt-level = "z" lto = true codegen-units = 1 panic = "abort" cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linu…

These look like sensible defaults to me for release mode. What are the tradeoffs?

Comparing with the current Cargo default [1]:

• `panic = "abort"` means that any panic terminates the program. This is not always desirable because you may want to catch and recover from panics, particularly in long-running servers.

• `strip = true` means that anything depending on DWARF would no longer work. Backtraces won't work, but also unwinding will no longer work (so this is disastrous if you haven't set `panic` above). The actual proposal has `strip = "debuginfo"` instead, so unwinding will work while backtraces won't.

• `codegen-units = 1` is the number of concurrent compilation jobs (cgu) in the LLVM codegen phase. A single cgu will significantly increase the compilation time, while allowing a bit more optimization. Otherwise this is okay.

• `lto = true` enables Rust-specific link-time optimizations across crates. The actual benefit depends on the set of crates linked, but it is significantly slower that many large enough projects wouldn't want it. It does benefit small programs like the "Hello, world" program the most though.

• `opt-level = "z"` is same to C/C++ `-Oz` and the same pros and cons apply.

[1] https://doc.rust-lang.org/cargo/reference/profiles.html#rele...

Re: Making Rust binaries smaller by default

#69
post #10
post #4

Initial binary size, like of a hello world, is a great indicator of how much abstraction the language has that youre also paying for. For example, java has to set up a constants pool, parse it from the classfile, run init and cinit, resolve references, allocatea a frame, and so on, just to get to the entrypoint. Compared to C without stdlib which needs to basically just run a few bytes to syscall write(). There are o…

I'm not trying to be rude, but what does your comment have to do with the actual article linked. I suspect you're commenting based solely on the title? If you read the article it mentions 90% reduction if you remove the debug symbols from the rust std lib that get bundled in the final hello world binary. A conversation about Java's size and abstraction is quite irrelevant to the article.

My comment is pretty unrelated to the content of the article, but more has to do with what others said here in the comments

Re: Making Rust binaries smaller by default

#70
post #64
post #58

Earlier quoted context omitted.

opt-level = "z" may be slightly slower than O2/O3. lto = true and lto-units = 1 makes the final linking unbearably slow for large programs. These are still okay, but then `panic = "abort"` shakes a lot of code off by making the final executable unable to print a nice stack trace (even without symbols) and instead immediately traps/abort()-s. Edit: and GP also used "panic-immediate-abort", which also removes the depen…

Additionally the panic = "abort" disables stack unwinding, and running destructors when panic occurs, so it's a big change to the semantics of the program. It's comparable to C++ land -fno-exceptions (not exactly, but similar).

IMHO when a piece of code decides to panic it should only happen when execution cannot continue under any circumstances, and in such cases all bets are off whether any cleanup code would actually still work. A hard abort might indeed be the best option.
Post reply on HN