https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125970

--- Comment #4 from vekumar at gcc dot gnu.org ---
(In reply to cuilili from comment #3)
> For option -O2 -mtune=generic -march=x86-64-v3, commit causes a ~15%
> regression on the HINT benchmark. The commit converts the branch inc =
> (errio < errjo) + 1 in the hot loop into a cmov/setcc sequence.
> 
> HINT's hot loop is memory-bound ("load a batch → little FP math → store a
> batch"), so its performance depends on MLP — keeping many memory accesses in
> flight so their latencies overlap.
> 
> Branch version: the branch is well-predicted, so the CPU speculates past the
> compare and computes these addresses early → memory accesses issue ahead of
> time → high MLP, latencies hidden.
> 
> cmov version: the cmov sequence itself is not expensive, but the selected
> value is used directly as a memory address index for the following accesses,
> it cannot be speculated. Address generation is pinned to the critical path
> (compare → setcc → cmov → lea), so the dependent accesses can no longer
> issue early. Overlap drops and MLP collapses.
> 
> cmov version:
> 
> comisd  %xmm3,%xmm1           
> seta    %r10b                 
> cmova   %r12d,%r15d           
> movzbl  %r10b,%r10d  --- %r10 is used as an array index for the following
> stores.        
> lea     0x1(%r10,%rsi,1),%r10d --- r10 ← r10+rsi+1  = index A
> movsd   %xmm3,(%r9,%r10,8)     --- store, uses index A
> mov     %r11d,0x0(%rbp,%r10,4) --- store, uses index A
> lea     (%rsi,%r15,1),%r10d    --- r10 ← rsi+r15    = index B, new r10,
> break the dependency
> movsd   %xmm1,(%r9,%r10,8)     --- store, uses index B
> mov     %ecx,0x0(%rbp,%r10,4)  --- store, uses index B
> mov     %esi,%r10d    ---r10 ← esi  new r10,breaks the dependency.         
> movsd   0x8(%rax),%xmm9
> mulsd   %xmm2,%xmm1
> ucomisd 0x10(%r9,%r10,8),%xmm4
> setp    %r10b                 
> movzbl  %r10b,%r10d           
> cmovne  %r12d,%r10d           
> add     %esi,%r10d 
> -----------------------------------------------------------------------    
> 
> It is hard to add a heuristic:
> 
> When the cmov's result feeds address computation, a misprediction is also
> more expensive than usual (speculative memory accesses on the wrong path
> must be squashed and redone). It is a trade-off that depends on
> predictability — cmov wins when the branch is poorly predicted, the branch
> wins when it is well-predicted — and static analysis cannot decide it
> without profile data.
> 
> Root limitation: static prediction cannot reflect real run-time behavior.
> 
> GCC's static branch-prediction probability for this branch:
> 
> if (errio < errjo)
>     goto <bb 7>; [50.00%]
> else
>     goto <bb 6>; [50.00%]
> 
> GCC estimate: 50% / 50%
> Reality (perf): 9.53% / 90.47% taken ratio — the branch is highly
> predictable. (~99% correctly predicted).

Yes this matches my observations.  If we want to stop "cmov" generation, for
this case, I can think of high-level heuristics like the candidate cmov result
is used to further index in loads/stores or feeds another cmov in a loop.

Reply via email to