13

The often known as likely and unlikely macros help the compiler know whether an if is usually going to be entered or skipped. Using it results in some (rather minor) performance improvements.

I started using them recently, and I'm not sure how often should such hints be used. I currently use it with error checking ifs, which are usually marked as unlikely. For example:

mem = malloc(size);
if (unlikely(mem == NULL))
  goto exit_no_mem;

It seems ok, but error-checking ifs happen quite often and consequently the use of the said macros.

My question is, is it too much to have likely and unlikely macros on every error-checking if?

While we're at it, what other places are they often used?


In my current usage it's in a library that makes an abstraction from the real-time subsystem, so programs would become portable between RTAI, QNX and others. That said, most of the functions are rather small and directly call one or two other functions. Many are even static inline functions.

So, first of all, it's not an application I could profile. It doesn't make sense to "identify bottle-necks" since it's a library, not a standalone application.

Second, it's kind of like "I know this is unlikely, I might as well tell it to the compiler". I don't actively try to optimize the if.

8
  • 7
    reeks of micro optimizing to me... Mar 1, 2013 at 11:40
  • 2
    For application code, I'd add them only if profiling showed that this code is used in a hot path. Mar 1, 2013 at 11:59
  • akkadia.org/drepper/cpumemory.pdf Page 57
    – James
    Mar 1, 2013 at 12:33
  • @james, that just says likely and unlikely exist and what they do. I didn't find anything that would actually suggest when and where it is best to use them.
    – Shahbaz
    Mar 1, 2013 at 13:25
  • @Shahbaz "If the condition is frequently false, the execution is not linear. There is a big chunk of unused code in the middle which not only pollutes the L1i due to prefetch- ing, it also can cause problems with branch prediction. If the branch prediction is wrong the conditional expression can be very inefficient." So, tight loops where you want to make sure the instructions you need are in L1i cache
    – James
    Mar 1, 2013 at 14:16

2 Answers 2

12

Do you need performance that badly that you're willing to pollute your code with that? It's a minor optimization.

  • Does the code run in a tight loop?
  • Does your application have performance problems?
  • Have you profiled your application and determined that this particular loop costs a lot of CPU time?

Unless you can answer yes to all the above, don't bother with stuff like this.

Edit: in response to the edit. Even when you can't profile, you can usually estimate hotspots. A memory allocation function that is called by everyone is a good candidate, especially since it requires only a single use of the macro to work for the whole library.

4
  • 1
    Just to be clear, I didn't down-vote you. However, your answer doesn't really answer my question. Are you trying to say that (un)likely macro is rarely used and only in extremely performance critical code? Is it "bad practice" to use it often, or just "unnecessary"?
    – Shahbaz
    Mar 1, 2013 at 12:22
  • @Shahbaz It makes the code less readable and the performance optimization may range from trivial gain to trivial loss. The latter when the assumption about the likelihood was either incorrect or has changed to be incorrect due to a later change to other parts of the code. If should never be used unless needed.
    – Peter
    Jul 2, 2015 at 13:27
  • 3
    @Peter: While it's too bad the syntax isn't nicer, notations as to what is likely or unlikely may provide useful information to humans who are reading code. For example, someone who saw if (likely(x==2 || x==3)) doOneThing(); else switch(x) { ... }, might judge that the programmer's use of an if for the values 2 and 3 was not merely a consequence of the programmer's not knowing that C can associate two case labels with a single handler.
    – supercat
    Aug 1, 2015 at 20:32
  • 1
    No one has mentioned what I feel is a critical point. It's not just that the "unlikely" path happens less often, it may be that conditioned on that path occurring you do not care about speed at all. For instance, a peripheral becomes unresponsive so you have to reset it and sleep anyway. Sep 5, 2019 at 19:08
2

If you're writing for x86/x64 (and are not using 20-year-old CPUs), the performance gain from using __builtin_expect() will be negligible if any. The reason for it is that modern x86/x64 CPUs (not 100% sure about Atom though), have dynamic branch prediction, so essentially CPU "learns" about the branch which is taken more often. Sure, this information can be stored only for a limited number of branches, however, there are only two cases possible. If (a) it is a "frequently used" branch, then your program will benefit from that dynamic branch prediction, and if (b) it is "rare" branch, you won't really see any realistic performance hit due to mispredictions in such rare branches (20 CPU cycles of branch misprediction are not TOO bad if it happens once in a blue moon).

NB: this does NOT imply that on modern x86/x64 importance of branch misprediction got lower: any branch with 50-50 chance of jump-nojump will still incur a penalty (IIRC 10-20 CPU cycles), so in inner loops branches may still need to be avoided. It is only importance of __builtin_expect() on x86/x64 which has diminished (IIRC, around 10-15 years ago or so) - mostly because of dynamic branch prediction.

NB2: for other platforms beyond x86/x64, YMMV.

4
  • Well, the compiler knows which branch the cpu will expect to be less likely. And it can arrange for that to be the actually unlikely one. But the compiler probably already knows that pattern without the unlikely-notation. Jul 17, 2018 at 17:08
  • @Deduplicator: with dynamic branch prediction, compiler doesn't know which branch is more likely as CPU calculates it in runtime based on previous runs through this very point in the code. Nov 4, 2018 at 15:50
  • I thought it's not really about telling the CPU what's likely, but about how the compiler is allocating the registers. If some execution is rare it doesn't matter when the execution solely runs on the stack instead within the registers.
    – Cosinus
    Feb 12, 2021 at 16:07
  • @Cosinus Historically, it was about avoiding branch mispredictions; however, in theory - you're right, but I am not sure if existing compilers take it into account. Feb 16, 2021 at 12:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.