Stream: t-libs

Topic: When to add #[inline]?


Lukas Kalbertodt (Feb 09 2020 at 10:15, on Zulip):

Do we have some clear rules when to add an #[inline] attribute to functions? #[inline(always)] and #[inline(never)] are only justified if there are good reasons (preferably benchmarks) to believe they improve performance/code size. The plain #[inline] on the other hand seems more confusing. "yes for non-generic functions", "sometimes also for generic functions", ...

Do we have a clear, precise guide on this?

simulacrum (Feb 09 2020 at 14:17, on Zulip):

IIRC @Alex Crichton was usually the go-to person on it, but we never wrote up clear guidelines.

My understanding is that:

Steven Fackler (Feb 09 2020 at 15:51, on Zulip):

IIRC #[inline] does increase the inlining priority of a generic function, so it can make sense to use there in some contexts, but you'd generally better have a good reason to do it.

simulacrum (Feb 09 2020 at 16:04, on Zulip):

Yes, it does. But usually we want to leave it to LLVM to decide that in std, at least, since it "knows better" in some sense -- mostly I think because std is used in everyone's code, so we need to be careful about things more so than random libraries

Lokathor (Feb 09 2020 at 17:40, on Zulip):

IIRC alex also based that advice on the idea that everyone will just use LTO all the time, which the default cargo profile.release doesn't actually do.

simulacrum (Feb 09 2020 at 17:44, on Zulip):

thinlto is enabled by default

simulacrum (Feb 09 2020 at 17:44, on Zulip):

you don't need full lto for these benefits afaik

simulacrum (Feb 09 2020 at 17:44, on Zulip):

and it's fine for us to be not at 100% perf due to missing annotations if lto is not enabled

simulacrum (Feb 09 2020 at 17:44, on Zulip):

people who want 100% perf imo should be using a custom profile and possibly even -Zbuild-std and so forth

Lokathor (Feb 09 2020 at 17:57, on Zulip):

The documentation states that thinlto is only between codegen units, not between crates.

simulacrum (Feb 09 2020 at 18:01, on Zulip):

that's true, but generics and #[inline] things get codegen'd into your crate

Tim Vermeulen (Feb 09 2020 at 20:44, on Zulip):

So for generic function #[inline] increases the inlining priority, but it still doesn't force it to be inlined? I used to think that #[inline] on a generic function did nothing, but I could have misunderstood

Simon Sapin (Feb 09 2020 at 21:01, on Zulip):

My understanding is that it also boosts some LLVM heuristic

simulacrum (Feb 09 2020 at 21:13, on Zulip):

Yes, it both makes the item available for inlining (already true of generics) and bosts an llvm heuristic

Hanna Kruppe (Feb 09 2020 at 21:45, on Zulip):

However, also keep in mind that rustc's codegen strategies have become more nuanced over time. For example, there's now a mode (-Zshare-generics) that reuses monomorphizations already created in dependencies where possible, though it's only enabled by default at opt-level={0,1,s,z} today. So the old heuristic "generic = available for inlining everywhere" no longer necessarily holds.

simulacrum (Feb 10 2020 at 14:11, on Zulip):

Yes, I think we don't care about non-opt-level=3 performance that much, in practice, but that's definitely true :)

Alex Crichton (Feb 10 2020 at 16:26, on Zulip):

The last I wrote up (I think?) about inlining stuff is at https://github.com/rust-lang/hashbrown/pull/119. Basically what was said previously is correct. You never need #[inline(always)] unless it's like an ABI thing. You probably don't ever need #[inline(never)] unless you actually see LLVM making wrong decisions (we could probably remove #[inline(never)] from #[cold] functions, those attributes are likely basically synonymous). You also basically never need #[inline] unless benchmarks show differently.

There's legitimate cases you need #[inline] such as tiny functions that aren't otherwise inlined, but I'd still recommend waiting for a benchmark to show anything before actually applying #[inline]. The downside of #[inline] is that increases compile times, and if you add it everywhere it can actually lead to very noticeable increases in compile time.

XAMPPRocky (Feb 10 2020 at 16:37, on Zulip):

There is documentation that KodrAus added to the libs team docs on forge, I don't how much of that matches the current discussion though. https://forge.rust-lang.org/libs/maintaining-std.html#is-that-inline-right

Elichai Turkel (Feb 11 2020 at 11:15, on Zulip):

@Alex Crichton So why almost all the user facing functions in std have inline attributes?

simulacrum (Feb 11 2020 at 12:36, on Zulip):

Many of them were likely added from benchmarks, though some look to be at least plausibly wrong

Alex Crichton (Feb 11 2020 at 17:38, on Zulip):

@Elichai Turkel agreed with @simulacrum, they're probably either erroneous, justified by a benchmark since forgotten, or added at a time it was more necessary than it is today. Consistency is definitely not a quality anyone would label libstd's usage of #[inline] as

Lokathor (Feb 11 2020 at 17:52, on Zulip):

There are times when even debug performance can also be a concern, perhaps some of them are from that.

Simon Sapin (Feb 11 2020 at 21:00, on Zulip):

Does #[inline] do anything with opt-level=0?

simulacrum (Feb 11 2020 at 21:02, on Zulip):

We explicitly currently do not optimize for debug performance in the standard library, generally speaking.

I am not sure about opt-level=0 and inline.

Hanna Kruppe (Feb 11 2020 at 21:07, on Zulip):

opt-level=0 doesn't run the inlining pass (only AlwaysInliner), but I believe it still in general has to codegen inline functions in crates using them, so code size and build times get worse for no benefit. Maybe upstream instances can be reused, if they exist and are exported, but as one often links release builds of libstd, libcore, etc. and copies of inline functions are internalized in normal release mode...

Lokathor (Feb 11 2020 at 21:42, on Zulip):

Hmm, unfortunate

Alex Crichton (Feb 11 2020 at 23:53, on Zulip):

@Simon Sapin in opt-level=0 mode as @Hanna Kruppe pointed out the inliner isn't even run, but the inlinehint LLVM attribute is indeed added. Additionally in release mode #[inline] causes translation into every single CGU, whereas in debug mode we do not do that and only translate into one CGU. Effectively, it's entirely ignored. (this is distinct from #[inline(always)])

Last update: Feb 25 2020 at 04:25UTC