Si consideri il seguente programma C:ramo statico previsione/ottimizzazione GCC
void bar();
void baz();
void foo(int a) {
if (a) {
bar();
}
else {
baz();
}
}
Sul mio computer x86-64-based, le istruzioni generate da GCC con il livello di ottimizzazione -O1 dà:
0: sub $0x8,%rsp
4: test %edi,%edi
6: je 14 <foo+0x14>
8: mov $0x0,%eax
d: callq 12 <foo+0x12> # relocation to bar
12: jmp 1e <foo+0x1e>
14: mov $0x0,%eax
19: callq 1e <foo+0x1e> # relocation to baz
1e: add $0x8,%rsp
22: retq
che, aggiungendo il parametro di ottimizzazione -freorder blocchi (incluso nel -O2) trasforma il codice in:
0: sub $0x8,%rsp
4: test %edi,%edi
6: jne 17 <foo+0x17>
8: mov $0x0,%eax
d: callq 12 <foo+0x12> # relocation to baz
12: add $0x8,%rsp
16: retq
17: mov $0x0,%eax
1c: callq 21 <foo+0x21> # relocation to bar
21: add $0x8,%rsp
25: retq
ciò che è principalmente un cambiamento da salta uguale a a salto non uguale a. So che fino al Pentium 4, la previsione statica delle diramazioni su un ramo condizionale in avanti non è stata presa dal processore (sembra che la previsione statica sia diventata casuale su altri processori Intel), quindi immagino che questa ottimizzazione si occupi di questo.
Supponendo che e riferendosi alla versione JNE ottimizzato, vorrebbe dire che il blocco altro è infatti considerato più probabile eseguito rispetto alla se blocco nel flusso del programma.
Ma cosa significa esattamente? Poiché non v'è alcuna ipotesi sul un valore nella funzione foo dal compilatore, tale probabilità si basa solo su scritti del programmatore (che potrebbero in realtà hanno usato if (!a)
invece di if (a)
e chiamate di funzione invertiti).
Ciò significa che dovrebbe essere considerata una buona pratica per il trattamento di se blocchi condizionali come casi eccezionali (e non il normale flusso di esecuzione)?
Cioè:
if (!cond) {
// exceptional code
}
else {
// normal continuation
}
invece di:
if (cond) {
// normal continuation
}
else {
// exceptional code
}
(ovviamente, si potrebbe preferire usando istruzione return all'interno del blocco rilevante per limitare le dimensioni indentazione).
Sì ... Ma i diversi punti di ritorno potrebbero essere stati realizzati anche su una versione 'je'. E GCC fa questo se/else blocca il riordino _consciamente_: nel programma iniziale cambiando il 'if (a)' a 'if (! A)' compila esattamente al contrario: da una versione 'jne' (non ottimizzata) ad un 'je' (versione ottimizzata per gli ordini). Non posso credere che GCC faccia questo cambiamento solo per prendermi in giro! :) – lledr
Penso che in ogni caso __builtin_expect dovrebbe aiutare questo ;-). http://blog.man7.org/2012/10/how-much-do-builtinexpect-likely-and.html –