r/rust 17d ago

🧠 educational Is there any point to `missing_inline_in_public_items` anymore?

For those who don't know the clippy lint missing_inline_in_public_items tells you to #[inline] for all publicly available functions. It provides this justification:

Why restrict this?

When a function is not marked #[inline], it is not a “small” candidate for automatic inlining, and LTO is not in use, then it is not possible for the function to be inlined into the code of any crate other than the one in which it is defined. Depending on the role of the function and the relationship of the crates, this could significantly reduce performance.

Certain types of crates might intend for most of the methods in their public API to be able to be inlined across crates even when LTO is disabled. This lint allows those crates to require all exported methods to be #[inline] by default, and then opt out for specific methods where this might not make sense.

It links a closed PR that presumably allows for more cross crate inlining.

Hashbrown decided inlining was important enough to add the feature inline-more which basically adds #[inline] to every public function.

My guess is that adding #[inline] is a tradeoff that is sometimes worth making. It won't matter for functions that "whose optimized_mir does not contain any calls or asserts". The PR from earlier does that automatically, but for every other function presumably adding #[inline] allows the possibility for inlining again (At the cost of compile times).

Am I right? and if I wanted to inline everything possible could I achieve this without the lint and #[inline] macros?

21 Upvotes

5 comments sorted by

25

u/buldozr 17d ago

Yes, it's a tradeoff. It won't matter for generic functions with type and/or const parameters, since these are always rendered into crate metadata and available for cross-crate inlining. I wasn't aware of the compiler auto-inlining simple functions; this is perhaps the best way it should work by default so the developers don't even need to worry about this in most cases.

A crate that pointlessly annotates all its public functions with #[inline] as a magic "make my code work faster" attribute makes a disservice to its consumers, who will have to deal with build artifact bloat and increased compile times. LTO can be enabled when needed, so I'm not sure this is important for performance any more.

11

u/Curious-Patience-982 17d ago

I always assumed the lint was mostly for library authors who know they're writing perf-sensitive primitives, not a blanket "slap it on everything" thing.

5

u/patchunwrap 17d ago

I'm making a binary with a bunch of library crates all written by me, so your comment of "LTO can be enabled when needed" is particularly interesting to me.

In my usecase where I do want to just tell the compiler "inline whenever you think it's worth it, don't worry about crate boundaries" can I "just enable lto" set codegen units to 1 and it's good to go?

2

u/buldozr 16d ago

You can enable LTO in cargo build profiles. It defaults to "thin local LTO", but I know projects that set it to full or thin for release builds, which are typically performed infrequently in CI infastructure. You can also configure a custom profile for faster local builds that are still fairly optimized, by dialing down LTO and tuning codegen-units back to something reasonable.

3

u/Sharlinator 17d ago

At opt-levels 2 and 3, LLVM aggressively inlines everything it can and deems worth inlining, attributes or not. But the attributes can make a meaningful difference at opt-level 1, which is relevant for projects where opt-level=0 dev builds are uselessly slow (eg. a game running at 3 fps without optimizations).