News for hackers. Stuff that matters.
Re:Intel i7 loop performance anomaly by rayiner on Tuesday December 03, 2013 @02:42PM (#6842617)
Don't have a compiler handy (or for that matter anything more recent than a Core 2 Duo), but the thing to do would be to replace the call instruction with a same-length sequence of NOOPs to see if its an instruction alignment issue. He mentions making sure the loop header is aligned, but there are other alignment issues in play.

For example, in Intel processors with a u-op cache, a 32-byte fetch group that generates more than 18 u-ops has to go through the x86 decoder and cannot stream from cache.[1] The call instruction, being a pretty big instruction with that 32-bit immediate displacement, might force the loop onto two 32-byte lines, reducing the u-op count in each line below 18 and allowing the loop to stream from the u-op cache instead of going through the decoders.

In general, if adding instructions makes a loop go faster, it's some sort of alignment (maybe code layout is a better term?) issue. Intel CPU's have limits on things like the number of branch predictions per fetch group, number of decoded instructions per fetch group, etc. Some x86 CPUs can only track 3 branches in a 16-byte group, while it might be possible to encode 4 in unusually dense code. In such cases, the extra branch just won't be tracked by the predictor. Seemingly superfluous code can force some of the branches into a different 16-byte group, improving overall performance. (This can't be the case here, because there's just one branch, but it can pop up in branch-heavy code).

EDIT: Someone makes in the comments makes a good point that in any case, both loops should fit in the loop buffer, which is after the u-op cache, and so wouldn't suffer from the 18-uop limit mentioned above.

[1] See: http://www.realworldtech.com/sandy-bridge/4

Re:Intel i7 loop performance anomaly by cperciva on Tuesday December 03, 2013 @03:09PM (#6842821)
In general, if adding instructions makes a loop go faster, it's some sort of alignment issue.

Not necessarily. In the early days of the P6 core, I found a simple loop which would take either 4.0 or 5.33 cycles depending on the internal CPU state -- exactly the same code, but running with an iteration count of 10^6 would take either 4000000 cycles or 5333333 cycles.

I had the good fortune to talk to a member of the team which designed the P6 core, and we concluded that this was due to a quirk of the out-of-order scheduler: In order to reduce circuit depths, the mechanism for finding and dispatching runnable uops wouldn't always dispatch the earliest available uops, and so -- depending on how the incoming uops were aligned within an internal buffer -- the P6 core would sometimes take a stream of instructions which could be executed in-order without pipeline stalls and run them out-of-order with less ILP.

In short, OOO cores are weird and horribly complicated and completely untrustworthy where performance is concerned.

Re:Intel i7 loop performance anomaly by pron on Tuesday December 03, 2013 @05:13PM (#6843566)
> In short, OOO cores are weird and horribly complicated and completely untrustworthy where performance is concerned.

Yep. There's a great talk about this by Cliff Click, called A Crash Course in Modern Hardware[1] that I would recommend to everyone. I am no hardware expert, so that talk really enlightened me.

Regarding the issue at hand, I remember Doug Lea saying[2] that some new Intel processors may recognize a loop as the OSs idle loop and power down the core. That's why he computes random numbers in busy-wait loops.

[1] http://www.infoq.com/presentations/click-crash-course-modern...

[2] http://emergingtech.chariotsolutions.com/2013/04/phillyete-s...

Re:Intel i7 loop performance anomaly by scott_s on Tuesday December 03, 2013 @04:22PM (#6843281)
Your last sentence is so important, and was basically what I was thinking the entire time reading that post, that I want to repeat it: In short, OOO cores are weird and horribly complicated and completely untrustworthy where performance is concerned.
Re:Intel i7 loop performance anomaly by dman on Tuesday December 03, 2013 @03:12PM (#6842843)
With Atom switching to OOO are there any mainstream processors left that are in order ?
Re:Intel i7 loop performance anomaly by djcapelis on Tuesday December 03, 2013 @03:35PM (#6843001)
Mainstream desktop processors? Not really. But there's tons in the embedded and mobile worlds. (All the ARM A7 cores, for instance.)

In order comes back during strange times. Sun switched back to in-order for Niagara, for instance.

Re:Intel i7 loop performance anomaly by m_mueller on Tuesday December 03, 2013 @04:26PM (#6843304)
GPUs are generally in order as far as I know - it only does OOO optimization at compile time. In general I like high performance programming much more using CUDA than on CPU - the performance is usually much more predictable and as such the time investment tends to correlate pretty well with performance improvements.
Re:Intel i7 loop performance anomaly by seanmcdirmid on Tuesday December 03, 2013 @07:22PM (#6844202)
GPUs are not only out of order but extremely synchronous in general with a restricted memory model that prevents memory stalls. Not only that, but multiple cores share the same control unit, which is why coherent branching across the cores is such a big deal (they all have to branch the same way or both branches have to be executed...).
Re:Intel i7 loop performance anomaly by seanmcdirmid on Tuesday December 03, 2013 @09:44PM (#6844659)
Edit: I meant to say "GPUs are not only in order", they are definitely not out of order.
Re:Intel i7 loop performance anomaly by m_mueller on Wednesday December 04, 2013 @02:21AM (#6845510)
Coherent branching is also important on CPU to make use of your vector units, on GPU the payoff is just much higher. Furthermore you never have to do silly things like loop unrolling for vector unit optimization since you write all kernels as if they were executed scalar. Once coherent branching is not possible anymore you're usually dealing with random access over your data - even then the higher random access memory bandwidth can get you a speedup over one CPU socket, compared to dual socket you're about even.

Bottom line is you just have to think about GPUs in a slightly different way - they do have disadvantages (limited register/cache/memory per thread since you need so many of them), but things like branching aren't that big of a deal.

Re:Intel i7 loop performance anomaly by kevinnk on Tuesday December 03, 2013 @03:31PM (#6842965)
Many ARM processors are in order. Also Itanium.
Re:Intel i7 loop performance anomaly by scott_s on Tuesday December 03, 2013 @04:10PM (#6843213)
Itanium may be in order, but if we're going for ease-of-reasoning, seek your fortunes elsewhere. The memory model gave me fits.
Re:Intel i7 loop performance anomaly by jevinskie on Tuesday December 03, 2013 @10:12PM (#6844759)
The PS3's Cell processor is in-order (both the PowerPC cores and the SPU co-processor cores) but that is getting to be a decade old.
Re:Intel i7 loop performance anomaly by hrjet on Tuesday December 03, 2013 @08:32PM (#6844461)
The processor industry is a mess (especially x86). The micro-architectures are fine tuned for benchmarks, at the expense of clean and reliable design.
Re:Intel i7 loop performance anomaly by pbsd on Tuesday December 03, 2013 @04:04PM (#6843182)
My first thought was uop cache as well. The fact that pre-Sandy Bridge processors have the intuitive performance behavior makes me more certain it's related.

One interesting experiment to try would be to realign the loop to 0..31 mod 32, and see how/if the behavior changes.

Re:Intel i7 loop performance anomaly by nkurz on Wednesday December 04, 2013 @03:55AM (#6845773)
both loops should fit in the loop buffer, which is after the u-op cache, and so wouldn't suffer from the 18-uop limit mentioned above

Both loops should fit, but I think the same limits for micro-op density apply. The Optimization Manual says the Loop Stream Detector requires that "All micro-ops are also resident in the Decoded ICache". I took this to mean if something can't be cached by the Decoded ICache, it can't be streamed by the LSD.

Re:Intel i7 loop performance anomaly by stingraycharles on Tuesday December 03, 2013 @03:10PM (#6842830)
That's is already being discussed in the comments, and the author claims NOOP-aligning the code has no effect.
Re:Intel i7 loop performance anomaly by rayiner on Tuesday December 03, 2013 @03:12PM (#6842840)
You wouldn't NOOP-align the loop header, but instead replace the CALL instruction in the middle of the loop with an equivalent sequence of NOOPs. This would increase the distance between the loop header and the backward jump.
Re:Intel i7 loop performance anomaly by mgraczyk on Tuesday December 03, 2013 @03:20PM (#6842888)
Is the 18 u-op limit still the case for Haswell?
Re:Intel i7 loop performance anomaly by nkurz on Tuesday December 03, 2013 @07:50PM (#6844320)
No, there is no such limit, but it's confusing.

Core2 had an 18 x86 instruction "loop stream detector".

Nehalem went to a 28 u-op (not instruction) LSD.

Sandy Bridge has a 1500 u-op "decoded instruction cache" plus a 28 u-op LSD.

Haswell is the same as SB, but doubles the u-op LSD to 56 if hyperthreading is disabled.

The confusing part is why there is still a tiny loop stream detector in addition to the much larger decoded iCache. Perhaps there is a small additional power savings for code that fits the smaller LSD?

But in general, you no longer have to worry about instruction decoding speed if your loops are less than 1500 u-ops. With some caveats: https://www-ssl.intel.com/content/dam/www/public/us/en/docum...

Re:Intel i7 loop performance anomaly by rayiner on Tuesday December 03, 2013 @10:56PM (#6844952)
The 18-uop limit comes from how Sandy Bridge (and Haswell) handle the decoded instruction cache. A 32-byte line of the instruction cache can map up to 3 lines in the u-op cache, each of which can hold 6 instructions. Any 32-byte line of instructions that decodes to more than 3x6 = 18 u-ops cannot be stored in the u-op cache.