'Emulate attribute "unpredictable"
There are [[likely]] and [[unlikely]] attributes in modern C++. There are corresponding __builtin_expect(x, 1) and __builtin_expect(x, 0) builtins in G++ and clang++. But also there are __builtin_unpredictable(x) and __builtin_expect_with_probability(x, 1, 0.5) or (equally) __builtin_expect_with_probability(x, 0, 0.5) builtins, which tells compiler to prevent CPU to fill pipeline with instructions from (mis)predicted branch, because the cost of flush+recovery of the pipeline from mispredicted path is statistically greater than execution w/o speculative execution at all.
Would be the using of [[likely]] or equally [[unlikely]] attributes on both if and else branches like in following snippets an equivalent of the using of hypothetical [[unpredictable]] attribute?
if (x) [[likely]] {
// "if" branch
} else [[likely]] {
// "else" branch
}
or
if (x) [[unlikely]] {
// "if" branch
} else [[unlikely]] {
// "else" branch
}
As I know if branch is treated by compilers as [[likely]] by default if there is else, and [[unlikely]] if there no else (because it is often the form of unhappy path checking with early exit from the current function). So if I just omit either of attributes, then it is not equivalent to specify hypothetical [[unpredictable]] attribute.
Solution 1:[1]
It seems marking both if and else branch with likely gets a warning (unlikely the same):
main.cpp:9:27: warning: both branches of ‘if’ statement marked as ‘likely’ [-Wattributes]
9 | if (i*j == p) [[likely]]
| ^~~~~~~~~~
10 | return 0;
11 | else [[likely]]
| ~~~~~~~~~~
So I guess the question remains hypothetical. As for a minimal complete example, it seems g++ supports [[likely]]and [[unlikely]] but not __builtin_unpredictable. Similarly, clang++ supports __builtin_unpredictable but not [[likely]] nor [[unlikely]] (at least clang 10 doesn't. So comparing all three options is tricky. Here is a minimal complete example that compares [[likely]] versus [[unlikely]]:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
bool isPrime(int p) {
for (int i=2;i<p;i++)
for (int j=2;j<p;j++)
if (i*j == p) [[likely]]
return 0;
// else [[unlikely]] <--- gets a warning
// j++; <--- remove from for loop
return 1; }
int main(int argc, char **argv) {
int numPrimes=0;
int start=atoi(argv[1]);
int end=atoi(argv[2]);
for (int p=atoi(argv[1]);p<atoi(argv[2]);p++)
if (isPrime(p)) { numPrimes++;}
printf("There are %d primes between %d and %d",numPrimes,start,end);
}
And here is the evaluation:
$ g++ -std=c++2a -O3 main.cpp -o main
$ time ./main 2 10000
There are 1229 primes between 2 and 10000
real 1m16.083s
user 1m14.014s
sys 0m0.247s
$ sed -i "s/likely/unlikely/g" main.cpp
$ g++ -std=c++2a -O3 main.cpp -o main
$ time ./main 2 10000
There are 1229 primes between 2 and 10000
real 0m38.277s
user 0m38.100s
sys 0m0.012s
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
