Emulate attribute "unpredictable"

166 views Asked by At

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.

1

There are 1 answers

1
OrenIshShalom On

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