Does #[cold] #[inline] fn make sense?

I have a function that is expected to be called rarely, away from the happy path of the program. At the same time, the function is a trivial getter.

Does it make sense to tag it with both #[cold] and #[inline]?

What does #[cold] do exactly? If it only puts function definition in a cold section of the exe, then it probably conflicts with inlining. OTOH if it hints to optimizer that branches with this call are unlikely, and that survives inlining, then maybe it helps?

4 Likes

#[cold] is basically direct map to llvm's calling convention.
Do note that it affects how function is invoked (including how registers are preserved) hence inline is unlikely to make any sense for it. If it is just trivial getter (one line) then you might as well just use inline(always) instead of cold to just eliminate function call (well hopefully)

P.s. I actually suspect cold doesn't really automatically gets you branch prediction hint. It is a separate matter that can be achieved with likely/unlikely which are yet to be added in stable std.
But compiler can probably optimize it on its own if it sees branch with just cold function, although I would just suggest check assembly for particular case

P.s.s. On a bit of philosophy cold and inline logically makes a little sense. When you invoke cold function you should already brace for performance impact of it hence trying to mitigate it by inlining is not necessary a fitting to the goal here (i.e. you assume that function is not going to be called normally)

2 Likes

Here it seems to say that cold does not correspond to llvm's coldcc, https://reddit.com/r/rust/comments/gtcsem/what_does_cold_actually_do/

Importantly, is it in the same crate as its caller? Because everything is eligible for inlining by default, and LLVM will reliably inline sufficiently-trivial things even when they're unmarked.

cold marks all the basic blocks that dominate the call as cold, and thus any branches that lead to them as automatically-unlikely. It then uses that to structure the generated assembly so that the likely blocks are proximate -- if you've ever noticed that call paths which end up panicking are always at the end of the assembly for a function, out of the way of the normal paths, this is why.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.