https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125970
--- Comment #3 from cuilili <lili.cui at intel dot com> ---
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).