Why internal `assert_failed` function should not be inlined?

First, this question may not be very basic or useful, but I am curious about the reason behind rust's standard library design.

I was looking at the split_off method in Vec<T>, inside of it, I found the function named assert_failed marked with attributes #[cold] and #[inline(never)]. I know the meaning of both attributes but I could not find any clear reason for doing this here.

Is it related to some kind of optimization or debugging, or is there some other reason?

+ The most likely reason I can come up with is that this is related to backtracing, and these attributes are intended to enable logs such as alloc::vec::Vec<T,A>::split_off::assert_failed at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library/alloc/src/vec/mod.rs:2110:13 to be displayed when an assertion failed. However, I am not 100% sure.

Thank you for your help. (and please excuse any errors in my English)

This has 3 effects:

  • The assembly instructions for assert_failed will be generated only once, as opposed to for every T of the Vec<T>. This speeds up compilation and reduces the size of the generated binary.
  • It tells LLVM that the branch inside which assert_failed is called will almost never be hit, thus LLVM can optimize knowing that.
  • It tells LLVM to place the assembly instructions for the assert_failed function far from the "hot" assembly instructions, i.e. the ones that will likely be executede more often. This makes the program make more efficient use of the instruction cache.

Thank you for your helpful answer! now I think I almost understand.

I have one final question: Is the first effect(generating assembly only once) due to the fact that the code for assert_failed is separated into an inner function, which is guaranteed to never be inlined?
I've thought that an inner function is like a normal function but scoped to the containing function. Are these two attributes directly related to the first effect? (of course, they guarantee the assembly of the function will never be inlined though)

I am just curious about how the use of these two attributes and the separation of the function are exactly related to every three effects, accordingly.

Thank you again for your response, it was very helpful!

+ after a few hours of googling I understood all three effects clearly!
It seems making non-generic inner functions is quite a common technique.
(of course, two attributes are still useful in the original question)

For those who found this page while searching, the below links might be helpful.

That translates to the cold attribute in LLVM:

This attribute indicates that this function is rarely called. When computing edge weights, basic blocks post-dominated by a cold function call are also considered to be cold; and, thus, given low weight.

As a demonstration of what SkiFire13 said, indexing uses the same trick: https://rust.godbolt.org/z/aE17b5qPa

That way the branch predictor defaults to assuming those branches won't be taken, so speculation works better and thus the bounds checks are ≈free, and because the code is later the processor can avoid loading it into the instruction cache in the first place.


Thank you for your more detailed answer! I will check out the link you mentioned.

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.