likely/unlikely equivalent for MSVC
Asked Answered
R

8

63

GCC compiler supports __builtin_expect statement that is used to define likely and unlikely macros.

eg.

#define likely(expr)    (__builtin_expect(!!(expr), 1))
#define unlikely(expr)  (__builtin_expect(!!(expr), 0))

Is there an equivalent statement for the Microsoft Visual C compiler, or something equivalent ?

Refuel answered 17/9, 2009 at 18:32 Comment(6)
Vote to add this feature on the VS feedback site!Athalee
Note that Microsoft stated that they don't like these kind of optimizations here and here. It seems unlikely (no pun intended) that this will be added in the future. We want people to use profile guided optimizations instead of annotating their code by hand. See this blog post for more info. Profile counts don't lie (or rather, they lie much less than users do).Howland
see also BOOST_LIKELY and BOOST_UNLIKELYNeilla
@Howland all those links are now dead. Another answer: *The argument for not implementing this feature is that it is non-standard. MSVC is pushing to implement standards features, not extend the language in ways that are incompatible with other compilers. (We’ve done too much of that in our past.) There is a standards proposal to introduce such an attribute. When it is standardized, we will implement it: open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdfNeilla
@Neilla unfortunately there's no archive I know of for connect pages. On the connect page a Microsoft employee said that machines did a better job of optimizing, I guess we're getting a slightly different story now ("it's not standards compliant").Howland
@Neilla See also: this meta post on connect links.Howland
B
17

I say just punt

There is nothing like it. There is __assume(), but don't use it, it's a different kind of optimizer directive.

Really, the reason the gnu builtin is wrapped in a macro is so you can just get rid of it automatically if __GNUC__ is not defined. There isn't anything the least bit necessary about those macros and I bet you will not notice the run time difference.

Summary

Just get rid of (null out) *likely on non-GNU. You won't miss it.

Bezoar answered 17/9, 2009 at 18:58 Comment(7)
I have a hardware device, after ever function call I need to perform a check like safeCall(mDevice.doit()), my safeCall is in-lined which increases performance, but only when I have likely/unlikely branch. I want to say that these macros can be useful.Axil
"In general, you should prefer to use actual profile feedback for [branch prediction] (-fprofile-arcs), as programmers are notoriously bad at predicting how their programs actually perform." gcc.gnu.org/onlinedocs/gcc/Other-Builtins.htmlFrink
Well I do miss the unlikely intrinsic. Without PGO (which is a PITA) the stupid MSVC almost always orders instructions the wrong way.Athalee
Although you do advise against it, naming _assume as "close" in this context is a bit of an unlucky wording, since it is not even remotely similar. The unaware reader might interprete "can get incorrect code" wrongly. Its meanining is equivalent to __builtin_unreachable. Using it for branch hinting is not just dangerous, it is by all means, and always, incorrect.Splore
More precisely, _assume(cond) is semantically largely equivalent to if (!(cond)) __builtin_unreachable(); in GCC. Which means using it as a branching hint allows the compiler to delete the whole "unreachable" branch and the condition as well if it does not have side effects.Bodywork
That link is dead, here's the latest: learn.microsoft.com/en-us/cpp/intrinsics/assumeGipon
benchmark for __builtin_expect: https://mcmap.net/q/15101/-optimizing-a-branch-for-a-known-more-common-path https://mcmap.net/q/14994/-how-do-the-likely-unlikely-macros-in-the-linux-kernel-work-and-what-is-their-benefitNeilla
A
26

C++20 standard will include [[likely]] and [[unlikely]] branch prediction attributes.

The latest revision of attribute proposal can be found from http://wg21.link/p0479

The original attribute proposal can be found from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0479r0.html

Programmers should prefer PGO. Attributes can easily reduce performance if applied incorrectly or they later become incorrect when program changes.

Absent answered 24/6, 2018 at 10:11 Comment(2)
What are the chances of getting something like GCC’s __builtin_expect (aka likely/`unlikely)? The argument for not implementing this feature is that it is non-standard. MSVC is pushing to implement standards features, not extend the language in ways that are incompatible with other compilers. (We’ve done too much of that in our past.) There is a standards proposal to introduce such an attribute. When it is standardized, we will implement it: open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdfNeilla
Now implemented in MSVC, GCC, and Clang. Note that it’s impossible to use this with the ternary operator, for example.Brassica
T
20

According to http://www.akkadia.org/drepper/cpumemory.pdf (page 57), it still makes sense to use static branch prediction even if CPU predicts correctly dynamically. The reason for that is that L1i cache will be used even more efficiently if static prediction was done right.

Trussing answered 28/9, 2012 at 16:48 Comment(0)
B
17

I say just punt

There is nothing like it. There is __assume(), but don't use it, it's a different kind of optimizer directive.

Really, the reason the gnu builtin is wrapped in a macro is so you can just get rid of it automatically if __GNUC__ is not defined. There isn't anything the least bit necessary about those macros and I bet you will not notice the run time difference.

Summary

Just get rid of (null out) *likely on non-GNU. You won't miss it.

Bezoar answered 17/9, 2009 at 18:58 Comment(7)
I have a hardware device, after ever function call I need to perform a check like safeCall(mDevice.doit()), my safeCall is in-lined which increases performance, but only when I have likely/unlikely branch. I want to say that these macros can be useful.Axil
"In general, you should prefer to use actual profile feedback for [branch prediction] (-fprofile-arcs), as programmers are notoriously bad at predicting how their programs actually perform." gcc.gnu.org/onlinedocs/gcc/Other-Builtins.htmlFrink
Well I do miss the unlikely intrinsic. Without PGO (which is a PITA) the stupid MSVC almost always orders instructions the wrong way.Athalee
Although you do advise against it, naming _assume as "close" in this context is a bit of an unlucky wording, since it is not even remotely similar. The unaware reader might interprete "can get incorrect code" wrongly. Its meanining is equivalent to __builtin_unreachable. Using it for branch hinting is not just dangerous, it is by all means, and always, incorrect.Splore
More precisely, _assume(cond) is semantically largely equivalent to if (!(cond)) __builtin_unreachable(); in GCC. Which means using it as a branching hint allows the compiler to delete the whole "unreachable" branch and the condition as well if it does not have side effects.Bodywork
That link is dead, here's the latest: learn.microsoft.com/en-us/cpp/intrinsics/assumeGipon
benchmark for __builtin_expect: https://mcmap.net/q/15101/-optimizing-a-branch-for-a-known-more-common-path https://mcmap.net/q/14994/-how-do-the-likely-unlikely-macros-in-the-linux-kernel-work-and-what-is-their-benefitNeilla
P
9

I know this question is about Visual Studio, but I'm going to try to answer for as many compilers as I can (including Visual Studio)…

A decade later there is progress! As of Visual Studio 2019 MSVC still doesn't support anything like this (even though it's the most popular builtin/intrinsic), but as Pauli Nieminen mentioned above C++20 has likely / unlikely attributes which can be used to create likely/unlikely macros and MSVC usually adds support for new C++ standards pretty quickly (unlike C) so I expect Visual Studio 2021 to support them.

Currently (2019-10-14) only GCC supports these attributes, and even then only applied to labels, but it is sufficient to at least do some basic testing. Here is a quick implementation which you can test on Compiler Explorer:

#define LIKELY(expr) \
  ( \
    ([](bool value){ \
      switch (value) { \
        [[likely]] case true: \
          return true; \
        [[unlikely]] case false: \
          return false; \
      } \
    }) \
  (expr))
#define UNLIKELY(expr) \
  ( \
    ([](bool value){ \
      switch (value) { \
        [[unlikely]] case true: \
          return true; \
        [[likely]] case false: \
          return false; \
      } \
    }) \
  (expr))

Edit (2022-05-02): MSVC 2022 supports C++20, including [[likely]]/[[unlikely]], but generates absolutely terrible code for this (see the comments on this post)... don't use it there.

You'll probably want to #ifdef around it to support compilers that can't handle it, but luckily most compilers support __builtin_expect:

  • GCC 3.0
  • clang
  • ICC since at least 13, probably much longer.
  • Oracle Development Studio 12.6+, but only in C++ mode.
  • ARM 4.1
  • IBM XL C/C++ since at least 10.1, probably longer.
  • TI since 6.1
  • TinyCC since 0.9.27

GCC 9+ also supports __builtin_expect_with_probability. It's not available anywhere else, but hopefully one day… It takes a lot of the guesswork out of trying to figure out whether to use ilkely/unlikely or not—you just set the probability and the compiler (theoretically) does the right thing.

Also, clang supports a __builtin_unpredictable (since 3.8, but test for it with __has_builtin(__builtin_unpredictable)). Since a lot of compilers are based on clang these days it probably works in them, too.

If you want this all wrapped up and ready to go, you might be interested in one of my projects, Hedley. It's a single public-domain C/C++ header which works on pretty much all compilers and contains lots of useful macros, including HEDLEY_LIKELY, HEDLEY_UNLIKELY, HEDLEY_UNPREDICTABLE, HEDLEY_PREDICT, HEDLEY_PREDICT_TRUE, and HEDLEY_PREDICT_FALSE. It doesn't have the C++20 version quite yet, but it should be there soon

Even if you don't want to use Hedley in your project, you might want to check the the implementations there instead of relying on the lists above; I'll probably forget to update this answer with new information, but Hedley should always be up-to-date.

Politico answered 14/10, 2019 at 21:50 Comment(4)
MSVC 2022 supports [[likely]] and [[unlikely]] - but generates terrible code with your lambda macros. The code MSVC generates if you put the [[likely]] attribute directly in the code is much better.Talent
Even with optimization enabled? IIRC I tried it to verify it worked well, though (obviously) not with 2022. Anyways, good to know [[likely]]/[[unlikely]] work, I'll try to update Hedley soon.Politico
Yes, even with optimizations. MSVC 2022 seems to be unable to completely optimize away the switch-case. See godbolt.org/z/xhar5WWv5 The generated code improves dramatically when using if-else instead of switch-case. But in both cases, it loses the hint. Seems it cannot propagate it out of the inlined lambda. godbolt.org/z/fsqd4o5fMTalent
Okay, thanks! It looks like I didn't include this version in Hedley anyways (at the time only GCC supported it, and there wasn't a good reason not to just always use __builtin_expect), so no need to update it after all. I'll add a note to this answer and try to remember to file the issue with MS, though I'm not going to hold my breath for a fix :(Politico
I
7

According to Branch and Loop Reorganization to Prevent Mispredicts document from Intel:

In order to effectively write your code to take advantage of these rules, when writing if-else or switch statements, check the most common cases first and work progressively down to the least common.

Unfortunately you cannot write something like

#define if_unlikely(cond) if (!(cond)); else 

because MSVC optimizer as of VS10 ignores such "hint".

As I prefer to deal with errors first in my code, I seem to write less efficient code. Fortunately, second time CPU encounters the branch it will use its statistics instead of a static hint.

Italia answered 1/7, 2012 at 21:17 Comment(2)
In answer to Xentrax: Your statement regarding MSVC does not seem to match with my observation. I'm using VS 2010 and see the compiler generating a "jne" when using a normal if. But when using an else instead, the compiler generates a "je", and puts the else block after the main flow. So, with MSVC, your define does seems to work. Now if only I could find a statement from MS that this is intended, supported behavior...Apopemptic
@Ruben: I was exploiting this code-gen trick since at least MSVC 2005. It still works like this in all versions since.Hap
F
6

__assume should be similar.

However, if you want to do this really well you should use Profile Guided Optimization rather than static hints.

Flowerdeluce answered 17/9, 2009 at 18:34 Comment(3)
I think this might be dangerous. According to Microsoft: "Because the compiler generates code based on __assume, that code might not run correctly if the expression inside the __assume statement is false at run time."Bezoar
@Digital - Very true, the linked to MSDN article describes the pitfalls. Again, static hints like this should be avoided and you should try to use PGO if at all possible.Flowerdeluce
Sorry but PGO is PITA, especially in libraries of any decent complexity. I know better what is likely and what is unlikely in my own code.Athalee
I
5

Now MS said they have implemented likely/unlikely attributes

But in fact there isn't any different between using "likely" or not using.

I have compiled these codes and is produce same result.

    int main()
    {
        int i = rand() % 2;
        if (i) [[likely]]
        {
           printf("Hello World!\n");
        }
        else
        {
            printf("Hello World2%d!\n",i);
        }
    }
    int main()
    {
        int i = rand() % 2;
        if (i)
        {
           printf("Hello World!\n");
        }
        else [[likely]]
        {
            printf("Hello World2%d!\n",i);
        }
    }
int pdb._main (int argc, char **argv, char **envp);
0x00401040      push    ebp
0x00401041      mov     ebp, esp
0x00401043      push    ecx
0x00401044      call    dword [rand] ; pdb.__imp__rand
                                   ; 0x4020c4
0x0040104a      and     eax, 0x80000001
0x0040104f      jns     0x401058
0x00401051      dec     eax
0x00401052      or      eax, 0xfffffffe ; 4294967294
0x00401055      add     eax, 1
0x00401058      je      0x40106d
0x0040105a      push    str.Hello_World ; pdb.___C__0O_NFOCKKMG_Hello_5World__CB_6
                                   ; 0x402108 ; const char *format
0x0040105f      call    pdb._printf ; int printf(const char *format)
0x00401064      add     esp, 4
0x00401067      xor     eax, eax
0x00401069      mov     esp, ebp
0x0040106b      pop     ebp
0x0040106c      ret
0x0040106d      push    0
0x0040106f      push    str.Hello_World2_d ; pdb.___C__0BB_DODJFBPJ_Hello_5World2__CFd__CB_6
                                   ; 0x402118 ; const char *format
0x00401074      call    pdb._printf ; int printf(const char *format)
0x00401079      add     esp, 8
0x0040107c      xor     eax, eax
0x0040107e      mov     esp, ebp
0x00401080      pop     ebp
0x00401081      ret
Inequality answered 20/2, 2021 at 5:12 Comment(1)
This is correct. [[likely]] and [[unlikely]] are noops. The feature tracking this being hooked to the optimizer is here: developercommunity2.visualstudio.com/t/…Gebhart
H
2

As the question is old, the answers saying there's no [[likely]] / [[unlikely]] in MSVC, or that there's no impact are obsolete.

Latest MSVC supports [[likely]] / [[unlikely]] in /std:c++20 and /std:c++latest modes.

See demo on Godbolt's compiler explorer that shows the difference.

As can be seen from the link above, one visible effect on x86/x64 for if-else statement is that the conditional jump forward will be for unlikely branch. Before C++20 and supporting VS version the same could be achieved by placing the likely branch into if part, and the unlikely branch into else part, negating the condition as needed.

Note that the effect of such optimization is minimal. For frequently called code in a tight loop, the dynamic branch prediction would do the right thing anyway.

Helbonnas answered 26/8, 2021 at 11:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.