1

I was just reading something about attributes in C++ on cppreference. They mentioned the probably(true) attribute there and now I'm wondering what it's good for. Unfortunately I couldn't find further information on the web.

Is this some kind of branch prediction that a processor uses during execution?

Timo
  • 9,269
  • 2
  • 28
  • 58
  • 2
    It is exactly what it is - seeing hardware branch predictions – levengli Jan 15 '17 at 20:14
  • it is just an example of what an attribute that only applies to the conditional branches could look like, it does not really exist in that form (to the best of my knowledge) – Cubbi Jan 16 '17 at 06:47
  • I'm not sure if I understood you correctly, but I just compiled some code with this syntax: `[[probably(true)]] if (...) ... else ...` I didn't check the generated code but it compiled at least (using msc and c++14) – Timo Jan 16 '17 at 07:08
  • 1
    @Timo as the cppreference page you linked to says, "All attributes unknown to an implementation are ignored without causing an error" (the rule was added for C++17, but it just enforced previous intent and practice) – Cubbi Jan 16 '17 at 21:08
  • also, here's an early-stage proposal to add these: https://ctrychta.github.io/branch_hints_proposal.html - it's proposing simply `[[likely]]` and `[[unlikely]]` – Cubbi Jan 18 '17 at 03:22
  • FWIW, as of today, I don't see anything about `probably` on [that page](http://en.cppreference.com/w/cpp/language/attributes). – BeeOnRope Jan 18 '17 at 22:30

1 Answers1

6

Yes, exactly. It is used to give the compiler more information about the if statement so that it can generate the optimal code according to the target micro-architecture

While each micro-architecture has its ways to be informed about the likelihood of a branch, we can take a simple example from the Intel Optimization manual

Assembly/Compiler Coding Rule 3. (M impact, H generality) Arrange code to be consistent with the static branch prediction algorithm: make the fall-through code following a conditional branch be the likely target for a branch with a forward target, and make the fall-through code following a conditional branch be the unlikely target for a branch with a backward target.

Simply put, the static prediction for forward branches is not-taken (so the code after the branch is speculatively executed, it's the likely path) while for backward branches is taken (so the code after the branch is not speculatively executed).

Consider this code for GCC:

#define probably_true(x) __builtin_expect(!!(x), 1)
#define probably_false(x) __builtin_expect(!!(x), 0)

int foo(int a, int b, int c)
{
    if (probably_true(a==2))
        return a + b*c;
    else
        return a*b + 2*c;
}

Where I used the built-in __builtin_expect to simulate a [[problably(true)]].

This get compiled into

foo(int, int, int):
        cmp     edi, 2           ;Compare a and 2
        jne     .L2              ;If not equals jumps to .L2

        ;This is the likely path (fall-through of a forward branch)

        ;return a + b*c;

.L2:
        ;This is the unlikely path (target of a forward branch)

        ;return a*b + 2*c;

        ret

Where I spared you some assembly code.
If you replace the probably_true with probably_false the code becomes:

foo(int, int, int):
        cmp     edi, 2           ;Compare a and 2
        je     .L5               ;If equals jumps to .L5

        ;This is the likely path (fall-through of a forward branch)

        ;return a*b + 2*c;

.L5:
        ;This is the unlikely path (target of a forward branch)

        ;return a + b*c;

        ret

You can play with with this example at codebolt.org.

Margaret Bloom
  • 41,768
  • 5
  • 78
  • 124
  • Nice answer, thanks! So will branch prediction always use the insturction that follows the currently executed instruction as prediction path and the jump path as failure path? – Timo Jan 16 '17 at 07:01
  • 2
    @Timo It depends on the architecture (or better the micro-architecture), in x86, for forward jumps (which are statically predicted not taken) your statement is true, but only the first time the branch is seen. After the first time, the branch predictor tries to detect a pattern of the taken-not-taken behaviour of the branch. See [the relative article in Wikipedia](https://en.wikipedia.org/wiki/Branch_predictor) for a start point. – Margaret Bloom Jan 16 '17 at 08:23
  • 1
    Despite the advice in the architecture guide, on modern x86 it is my understanding that there are _no more static prediction rules!_. That is, the way modern branch predictors are designed, there will always only be a dynamic prediction: if the branch hasn't been seen, the prediction is just based on whatever junk is left in the prediction tables from other jumps that hashed there, or perhaps a default (probably fallthrough-always) prediction if nothing is there at all. – BeeOnRope Jan 18 '17 at 20:23
  • 1
    Fundamentally, static prediction rules come "too late" - by the time you've decoded the instruction, you have already fetched several lines of additional instructions. So the that's another reason static prediction is largely abandoned. The dynamic prediction mechanisms can work ahead of instruction fetch. The `__builtin_expect` expect stuff is still useful since it helps code generation - it's best for the most likely path to be the straightline path, and in general for the most likely basic blocks to be grouped together. That ends up _mostly_ dovetailing with the (old) Intel advice anyway! – BeeOnRope Jan 18 '17 at 20:26
  • @BeeOnRope Yes, I remember something about static prediction being abandoned. Maybe in a question/answer of yours? – Margaret Bloom Jan 18 '17 at 20:48
  • 1
    @MargaretBloom - could be, I can't even recall now where I read it either. Oddly, while playing around with your example, I found `icc` produces _terrible_ code (at least in the example I tried) if you use `__builtin_expect`. It goes from producing the [optimal code](https://godbolt.org/g/05P9mP) for this loop (versus `gcc` and `clang` which both fail and produce loops with 5 instructions), to totally terrible code no matter [which](https://godbolt.org/g/t6mxbv) [way](https://godbolt.org/g/RBXrjJ) you predict the exit. It adds a `cmov` and other garbage, perhaps due to the `!!(x)` part? – BeeOnRope Jan 18 '17 at 21:32
  • 2
    @BeeOnRope I can't tell for sure but it may be, the first instructions seem to set `eax = !!esi` – Margaret Bloom Jan 18 '17 at 22:19
  • 1
    That `icc` issue was weird enough that I made a [question about it](http://stackoverflow.com/q/41731642/149138). – BeeOnRope Jan 19 '17 at 00:09