Optimizing compilers have accumulated so much wisdom over the years of development that it’s hard to imagine anything they don’t do in 2022. For the most part it’s a safe assumption - from high-level optimizations solving problems that used to require Gauss’ genius to low-level assembly tricks that replace expensive arithmetic operations with simpler ones, compilers have our backs.
Today we are going to work on a very simple and common problem - checking if the integer is odd. Tasked with such problem, most would write
which is technically wrong for negative numbers. But since such checks are usually performed for indices, for now let’s pretend that only positive numbers are passed to this function and in our contract we declare that the behavior for negative numbers is undefined.
Now that we have this precondition we can focus on its efficiency and take a look at the generated assembly:
and that’s where we see the impact of the precondition that is not encoded in data types - instead of a trivial logical and with 1
and its assembly
compilers cannot guess our contract with users and have to properly handle the fact that the passed integer may be negative which adds overhead.
And in case you wonder if it makes any difference in practice - the answer is yes, 2.3X difference, in fact.
As such, we should always strive to use data types in a way that encodes as many domain knowledge as possible. In this particular case, since we expect non-negative numbers, a better parameter type would be unsigned int.
This trivial change
helps compilers to produce the assembly we expect
and desired benchmark results