#[cold] on match arms by x17jiri · Pull Request #120193 · rust-lang/rust (original) (raw)

Edit

This should be in T-lang. I'm not sure how I can change it.

There is discussion: https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Allow.20.23.5Bcold.5D.20on.20match.20and.20if.20arms

Summary

Adds the possibility to use #[cold] attribute on match arms to hint the optimizer that the arm is unlikely to be taken.

Motivation

These hints are sometimes thought to help branch prediction, but the effect is probably marginal. Modern CPUs don't support hints on conditional branch instructions. They either have the current branch in the BTB (branch prediction buffer), or not, in which case the branch is predicted not to be taken.

These hints are, however, helpful in letting the compiler know what is the fast path, so it can be optimized at the expense of the slow path.

grep-ing the LLVM code for BlockFrequencyInfo and BranchProbabilityInfo shows that these hints are used at many places in the optimizer. Such as:

History

RFC 1131 ( #26179 ) added likely and unlikely intrinsics, which get converted to llvm.expect.i8. However this LLVM instruction is fragile and may get removed by some optimization passes. The problems with the intrinsics have been reported several times: #96276 , #96275 , #88767

Other languages

Clang and GCC C++ compilers provide __builtin_expect. Since C++20, it is also possible to use [[likely]] and [[unlikely]] attributes.

Use:

if (__builtin_expect(condition, false)) { ... this branch is UNlikely ... }

if (condition) [[likely]] { ... this branch is likely... }

Note that while clang provides __builtin_expect, it does not convert it to llvm.expect.i8. Instead, it looks at the surrounding code and if there is a condition, emits branch weight metadata for conditional branches.

Design

Implementing likely/unlikely type of functions properly to emit branch weights would add significant complexity to the compiler. Additionally, these functions are not easy to use with match arms.

Replacing the functions with attributes is easier to implement and will also work with match.

A question remains whether these attributes should be named likely/unlikely as in C++, or if we could reuse the already existing #[cold] attribute. #[cold] has the same meaning as unlikely, i.e., marking the slow path, but it can currently only be used on entire functions.

I personally prefer #[cold] because it already exists in Rust and is a short word that looks better in code. It has one disadvantage though.
This code:

if cond #[likely] { ... }

becomes:

if cond { ... } #[cold] { ... empty cold branch ... }

In this PR, I implemented the possibility to add #[cold] attribute on match arms. Use is as follows:

match x {
    #[cold] true => { ... } // the true arm is UNlikely
    _ => { ... } // the false arm is likely
}

Limitations

The implementation only works on bool, or integers with single value arm and an otherwise arm. Extending it to other types and to if statements should not be too difficult.