I haven't been following that bug closely, but I think I'm OK on this machine since hyperthreading is turned off? This a a benchmarking machine, so I have to be careful about making changes that will affect historical results.
the general principle of stalling the frontend to prevent the backend being overloaded still holds
I think I agree that this is what is happening. Why it's necessary, and the exact mechanism by which it helps are places that I'm uncertain about. I presume you are seeing the instructions showing up as IDQ_MITE_UOPS?
From https://www.intel.com/content/www/us/en/architecture-and-tec... Section 2.3.2.2, here's some more limitations of the Decoded ICache:
The Decoded ICache consists of 32 sets. Each set contains
eight Ways. Each Way can hold up to six micro-ops. The
Decoded ICache can ideally hold up to 1536 micro-ops.
The following are some of the rules how the Decoded ICache
is filled with micro-ops:
• All micro-ops in a Way represent instructions which are
statically contiguous in the code and have their EIPs
within the same aligned 32-byte region.
• Up to three Ways may be dedicated to the same 32-byte
aligned chunk, allowing a total of 18 micro-ops to be
cached per 32-byte region of the original IA program.
• A multi micro-op instruction cannot be split across Ways.
• Up to two branches are allowed per Way.
• An instruction which turns on the MSROM consumes
an entire Way.
• A non-conditional branch is the last micro-op in a Way.
• Micro-fused micro-ops (load+op and stores) are kept as
one micro-op.
• A pair of macro-fused instructions is kept as one micro-op.
• Instructions with 64-bit immediate require two
slots to hold the immediate.
As theory would predict, I was able to get the "fast" speedy by forcing legacy decoding by adding a enough junk TEST/JZ pairs. The "non-conditional branch" limitation might explain why both JMP and CALL/RET have the same effect. I wonder if the occasional alignment effects I see have to do with splitting op-stores across a 32B boundary?Focus on port 4, the only port available for memory stores.
Yes, we'd expect 4B, and we instead see 1.6B for the fast case and 2.0B for the slow. My version above is at 1.4B, and I'd guess is proportionally faster. Have you seen tricks that reduce this to some lower number?
We'd also expect P2 plus P3 to equal 4B? It's interesting that these are closer, but still quite a bit over.
By dispatching fewer instructions that we can handle per cycle, there is less speculation and thus less wasted work?
While there is less waste in terms of power efficiency, are we actually seeing P4 be a bottleneck? It's hard to tell as the total cycles also goes up, but I think it's still at 90% rather than 100% utilization.
So while it looks bad, I'm not sure that anything will necessarily go faster if we reduce the number further. It may be that the maximum difference is whether we get a 4 cycle turnaround on the store-load forwarding versus a 5 cycle.