In previous articles we’ve already seen the power and simplicity of compile-time evaluation, so it’s very tempting to use it as much as possible. There is one problem though - what if we don’t know the inputs of our functions in advance and its range is too big to build a lookup table?
“It's tough to make predictions, especially about the future.”
― Yogi Berra
Well, the fact that we don’t know the future doesn’t stop humanity of one of its favorite activities - speculation. So how can we do the same in C++?
Let’s take the most abused problem of computing the nth fibonacci number as an example and to make matters even worse, its naive exponential implementation
We can see that even though the function fib is marked as constexpr, since we don’t know the value of the n in advance, the assembly looks predictably slow
But what if we know that the majority of invocations, or the ones that are most latency critical happen to use inputs 20 and 42?
Unfortunately clang ignores this “hint” and produces the exact same assembly, but gcc does what we want and adds special cases for our most important inputs
This doesn’t seem very useful for such a trivial example, but this approach can be extended to handle multiple template parameters and arbitrary complexity.
We can also force clang to play along by doing something like
to get
but as soon as we try to do the same for 42 we get
Bummer.
Well, in case we cannot use constexpr or want a guarantee that the computation happens at compile time even when using clang we can use the good old templates.
Now even with clang we are getting expected
Sweet! Similar approach is used by JIT for speculation of types of dynamic arguments and other aspects of runtime execution to use the most specialized versions of functions.