Change:
While playing with to see if it can be made branchless and thus a const fn (sorry ;) ), I found that the performance of signed can actually be improved. I tested two implementations: 2. The function acts kind of like C's ternary operator, including its constness, but works only for integers. The second case seems to have a reciprocal throughput better by a factor of about 1.5 according to llvm-mca: For unsigned, the second case can become: In this case, there is no performance improvement and LLVM reduces this to the same IR, so the only advantage here would be constness. I found some similar improvements for , even though currently the implementation uses ; I found it a bit weird that the intrinsic performs worse. The reciprocal throughput can be improved by a factor of 1.13: My concern is that since the LLVM IR is different, even though llvm-mca indicates the difference is an improvement, there might be some case I don't know about where the performance regresses. Maybe there are other platforms where the saturating intrinsics result better performance? I think has a better understanding of these concerns.
Edit: This comment is wrong. By the way, the "improved" above can be further improved. LLVM produces but I think can be changed to immediately, as the next time is used it is to do the subtraction anyway: Edit: Oops, the second subtraction is required for the instruction.
You can also use which compiles to the same thing (actually it seems like it outperforms the bit mask one at -O1)
Interesting, when compiling the function itself at -O0, the masks version performs better (masks 7, mul 9), at -O1 LLVM recognizes the multiplication pattern so it performs better (masks 5.2, mul 1), and at -O2 LLVM recognizes both patterns so that both have a reciprocal throughput of 1.
Yeah it seems the problem are the multiplications and additions that cause Rust to emit overflow checks at -O0.
The branchless versions also vectorize much better:
They seem to vectorize more, but the reciprocal throughput becomes worse. (I basically don't know anything about vectorization, so maybe rthroughput doesn't mean anything here, but maybe it does.)
It looks like the improvement to actually comes from replacing : with : Going further and replacing with gives exactly the same assembly as the branchless version above: So I think it would be worth changing that conditional even if we don't go all the way to .
Sounds good, I'll open a PR to change the multiplication as suggested.
About the subtraction, LLVM is producing the following assembly for its intrinsic: I think this is suboptimal, and followed by should be replaced by . I'll open an issue in LLVM.
I believe none of these functions care about perf at O0 or O1 much, as stdlib is always compiled with full optimization and these methods are not generic. Consider also verifying how the code behaves when inlined in other contexts that do or do not provide sufficient information to LLVM to optimise out interesting parts (branches and expensive operations) of the function.
Since the improvement in throughput I observed can be obtained by simply changing to , I opened a PR with just that change which I think looks quite safe and doesn't have much room for adverse effects.

Discussion:
#[inline] pub fn saturating_mul(self, rhs: Self) -> Self { self.checked_mul(rhs).unwrap_or_else(|| { <del> if (self < 0 && rhs < 0) || (self > 0 && rhs > 0) { </del> <ins> if (self < 0) == (rhs < 0) { </ins> Self::max_value() } else { Self::min_value()