How deep #[inline] is?


#1

What is the actual meaning of #[inline] attribute: does it work one level deep or does it tell the compiler to inline the whole call tree?

Consider the following code:

#[inline(always)]
fn foo(x: &mut u32) {
  bar(x);
}

fn bar(x: &mut u32) {
  *x += 1;
}

fn main() {
  let mut x = 0;
  foo(&mut x);
}

Is foo(&mut x) supposed to be inlined as bar(&mut x) or as x += 1?

In other words is it a bad practice to inline delegating methods when I don’t mean delegatee to be inlined.


#2

It’s one deep. And it’s only a hint.

Unless you use inline(always) or inline(never), the compiler will just make up its mind.

You should add such things only if you measure it actually helps ‒ inlining something might have even the very opposite effect or opposite effect on some other parts of the code.


#3

So if a function is only calling another function it is still dangerous to inline(always) without profiling first?


#4

Or if a function is just a getter or setter - is it a bad practice to blindly inline(always) it?


#5

The general advice is: if you have to ask whether you should use #[inline(always)], you shouldn’t use #[inline(always)]. Just use #[inline] and trust the compiler.


#6

Yes.

It won’t make things faster, but it’ll make your debug builds slower.

Also, the compiler will inline without any attribute for generics and inside a crate, so you often don’t even need the basic #[inline].

And if you’re tempted to run around adding it to things, it’s possible that what you actually want is LTO.

Edit: Haskell has some docs about this; what they say applies well to Rust too https://ghc.readthedocs.io/en/8.0.1/glasgow_exts.html#inline-pragma


#7

Got it. Thank you all for the answers!