How are the release builds identical?

I created a quick binary. One like this:

mod foo {
    mod bar {
        pub fn run() {
            panic!("uhoh!");
        }
    }
    pub fn run() {
        bar::run();
    }
}

fn main() {
    foo::run();
}

And another where those modules are split into files (foo.rs and foo/bar.rs). I used the following Cargo.toml to match the environment of a real project I'm investigating:

[package]
name = "foo"
version = "0.1.0"
edition = "2021"

[profile.release]
opt-level = 3
debug = false
rpath = false
lto = true
debug-assertions = false
codegen-units = 1
panic = 'abort'
incremental = false
overflow-checks = true

The debug build size was slightly different (as expected), while the release build size is 100% identical.

In theory, this is what I'd want, release builds to just care about the code and not the file structure.

However, with RUST_BACKTRACE=full , I am still able to see the line number of the panic, and when I open up a hex editor to see how it knows that- lo and behold, the different src/main.rs vs. src/foo/bar.rs strings are there.

Ok, but now I'm very confused - how they can still have identical filesize? there's clearly a diff in the binary too (using Hex Fiend I did a rough compare, and diff also reports that they differ)

Just to sanity check - by identical filesize I mean same value with ls -la as well as via OSX get info (just incase there is some misconception there).

More generally (for optimized release builds, e.g. with the above settings):

  1. to what extant does file naming and structure affect final output size?
  2. can this in any way affect performance?

I know that this level of optimization is silly for anything I'm targetting... not about to actually worry about this while naming things. But I am curious to understand what's happening at this level a bit better, just for the sake of learning.

Thanks!

The compiler generally performs aggressive inlining in an optimized build. It should not matter where you define a function within a crate. (This even extends to cross-crate inlining if either generics or LTO is involved.)

The resulting executable sizes can be the same eg. because of padding within the file. If, for example, debug info is arranged in 1k blocks, then it doesn't matter whether there is 1 byte of debug info or 1023 bytes, the remaining space will be padded. I don't know if this is actually what you are observing, but padding is common practice in formats intended to be loaded into memory verbatim.

3 Likes

Ahh, interesting... that does make sense. Much appreciated!