Fast and slow if-statements: branch prediction in modern processors
igoro.com
igoro.com
Micro-optimization is a silly diversion the vast majority of the time. Wait to optimize at that level until you have the tooling and the measurements that indicate you truly need it (more often than not you won't).
The article simply explains what branch prediction is and why CPUs implement it. No part of the article advocates that you attempt to optimize your code to exploit branch prediction. In fact, the conclusion of the article is that in the majority of real-world cases, the branch predictor will automatically do the right thing.
Hi!
That's true, there are other development tasks where the vast majority of developers should look at with greater priority.
Still, I'd like to suggest what is the main takeaway from the Igor's post, if you may.
I would like to put the emphasis on the role played on timings by the presence data-dependent branches, and not on how developers should try to optimize the performance by keeping the pipelines stages as busy as possible.
This have practical importance, for example, while implementing cryptographic algorithms or secret-dependent algorithms, as the timing patterns can be exploited by side channel attacks.
Nothing new, something already happened:
Predicting Secret Keys via Branch Prediction by Onur Aciicmez and Jean-Pierre Seifert and Cetin Kaya Koc http://eprint.iacr.org/2006/288.pdf
On the Power of Simple Branch Prediction Analysis by Onur Aciicmez and Cetin Kaya Koc and Jean-Pierre Seifert http://eprint.iacr.org/2006/351.pdf
Of course, Igor's post is not about cryptanalysis, agreed. Still provides some useful quantities relating mispredicted branch number to the spent cycles.
Cheers
I think that developers become better rather than worse when they start to understand what happens under the hood. Are we to protect people from the dangers of superficial levels of knowledge by keeping them ignorant? Teach them, and if that's not enough, then teach them more:
"Mispredicted branches can cause a conditional itself to run about 10 times slower. In very tight loops, this can be a problem, but most of the time this effect isn't even measurable. It's dwarfed by other effects, such as accesses to main memory, which are in turn dwarfed by disk access. If you read from memory in your routine more than a couple times, or touch disk even once, it's probably not worth worrying about branch prediction errors."
Or would you suggest that kernel programmers shouldn't be aware of these effects either? :)
The best-case scenario for this information is for the vast majority of developers to ignore it, or to read about it and then do absolutely nothing about "the problem".
Even in the rare case where you happen to be that one guy who's writing kernel, library, or game code which needs to be heavily optimized at every level this particular issue is extremely unlikely to be relevant. And even in the super rare cases where it is relevant the given article doesn't provide very good advice on improving performance (hint: for a lot of cases it'd be better to rewrite your loop so you don't even have a conditional inside it).
In short, this article is silly trivia, nothing more, and roughly equally useful to the practice of programming as looking at pictures of cats with funny captions, maybe less so.
P.S. The real risk here is that devs become overly focused on a "problem", lose their situational awareness and ignore more serious problems they should be concerned with. It's the sort of thing that causes pilots to crash planes while they are distracted by a warning light they can't figure out: http://en.wikipedia.org/wiki/Eastern_Air_Lines_Flight_401
I disagree about the "one guy" theory. While it may be true that very few Ruby programmers would benefit from this knowledge, if you are bothering to use C or C++ you might as well take full advantage of the speed it has to offer. If not, why not use a higher level language? I'd certainly want the author of my interpreter to be fully aware of these issues: http://bugs.python.org/issue4753
if(likely(condition)) dosomething();
http://kerneltrap.org/node/4705
I also have some other links :
These people saw 10% gain in their Scheme http://people.csail.mit.edu/jaffer/CNS/interpreter-branch
And this concerns reducing power consumption using Branch Prediction Prediction ! http://www.eecg.toronto.edu/~moshovos/research/iccd02.pdf
Furthermore each CPU uses different approaches to branch prediction such that a bad pattern for one will not be in another. So spending time trying to optimize your code in this way will only optimize it for a particular processor which wont work if you're trying to make a distributable binary. (i.e. different x86 processors use different branch prediction algorithms. You will basically be optimizing specifically for the Intel i7 or specifically for the AMD Athlon 3)
I have a feeling that they would notice repeated calls to the same if-statement and optimize accordingly. They may still have the same sorts of issues, but would the used benchmark (looping and querying the if-statement n times) have completely different results in a JIT system?
D:\p\badif>..\luajit\src\luajit badif.lua
(i & 0x80000000) == 0 time: 1.057
(i & 0xFFFFFFFF) == 0 time: 0.696
(i & 0x00000001) == 0 time: 3.682
(i & 0x00000003) == 0 time: 5.339
(i & 0x00000002) == 0 time: 3.686
(i & 0x00000004) == 0 time: 3.683
(i & 0x00000008) == 0 time: 3.686
(i & 0x00000010) == 0 time: 3.856
GCC has some macros where you can specify if a branch is likely or unlikely so the compiler will optimize the code for you based on which cpu you are compiling on, but at higher level languages you would have to do this by hand. The performance gain from this optimization will be negligible compared to if you had spent that time trying to optimize something else.
The macros you refer to a are an even more minor effect --- they only affect what happens the first time through the loop. It's a tiny optimization of an optimization. Branch prediction is a hardware feature, and is going to happen whether you use these macros or not. It's the logic of your code that's going to make the big difference, not the macros.
But I'd argue that if this level of optimization is required, the bigger gains are to be see through changing your algorithms to become more regular: for example, using fixed length blocks in a compression scheme rather than checking for null termination [2]. But if this is for some reason impossible, then use hinting!
Unless you're also concerned with how Python or Ruby stores its values internally, you don't need to think about the speed of conditionals. It's really only an issue if you're writing the Python or Ruby interpreter itself.