2014-11-25 18 views
6

Capisco quando una succursale è facilmente prevedibile è meglio usare un'istruzione IF perché il ramo è totalmente gratuito. Ho imparato che se il ramo non è facilmente prevedibile, allora un CMOV è migliore. Tuttavia, non capisco come questo possa essere raggiunto?Che cos'è il CMOV che migliora le prestazioni della pipeline della CPU?

Sicuramente il dominio del problema è sempre lo stesso: non conosciamo l'indirizzo della prossima istruzione da eseguire? Quindi non capisco fino in fondo la pipeline, quando viene eseguito il CMOV, in che modo ciò avrebbe potuto aiutare il feticcio dell'istruzione (10 cicli di CPU in passato) a scegliere il percorso corretto e prevenire uno stallo della pipeline?

Qualcuno potrebbe aiutarmi a capire come CMOV migliora la ramificazione?

+3

possibile duplicato di [Perché una mossa condizionale non è vulnerabile per errore di previsione ramo?] (Http://stackoverflow.com/questions/14131096/why-is-a-conditional-move-not-vulnerable-for-branch -errore-errore) –

+2

Come "non si conosce l'indirizzo della prossima istruzione da eseguire?" Se la sequenza di istruzioni è 'I1; cmov; I3', le istruzioni 'I1', quindi' CMOV', quindi 'I3' vengono eseguite, sempre in questo ordine. Quello che viene dopo 'I1' è' CMOV'. Quello che viene dopo 'CMOV' è' I3'. –

+0

Un ramo con una sola istruzione è un caso molto speciale; si presta a ottimizzazioni che un ramo di scopo generale non ammette. –

risposta

6

Le istruzioni CMOV non indirizzano il percorso del flusso di controllo. Sono istruzioni che vengono eseguite per calcolare il risultato in base a codici di condizione, ad esempio istruzioni predeterminate. Alcune architetture (come ARM) possono prevedere molte forme di istruzioni basate su codici di condizione, ma x86 può solo fare "mov", cioè il movimento condizionale (CMOV). Questi sono decodificati ed eseguiti con latenza per determinare il risultato dell'istruzione.

I rami, al contrario, sono previsti e guidano effettivamente l'esecuzione delle istruzioni. Il predittore di branche "guarda avanti" dell'istruzione "fetcher", in particolare alla ricerca di istruzioni di ramo, e predice il percorso guidando il flusso. Pensa a un binario ferroviario dove una persona in avanti sposta i binari a sinistra oa destra per dire al treno dove andare. Ora se il ragazzo ha scelto la direzione sbagliata, il treno deve fermarsi, fare il backup, quindi muoversi di nuovo nella giusta direzione. Un sacco di tempo sprecato.

I CMOV, d'altra parte, non governano il flusso. Sono semplicemente istruzioni che richiedono più tempo (e creano dipendenze aggiuntive) per capire il risultato corretto del movimento in base ai codici di condizione. Pensa al treno, invece di decidere di andare a destra o sinistra, imbocca una strada dritta che non richiede svolta, ma è un po 'più lenta (ovviamente molto più complicata, ma è la cosa migliore che riesco a fare adesso).

I CMOV erano veramente pessimi (latenza molto alta) ma da allora sono migliorati per essere abbastanza veloci, rendendoli molto più utilizzabili e performanti.

Spero che questo aiuti ..

+1

Buona risposta, anche se mi auguro che l'esempio di treno per cmov possa aver incluso spaccare i carrelli tra le ferrovie in qualche modo, con uno di loro alla fine precipitando in un abisso (ovviamente ci dovrebbe essere anche una scena di lotta sul tetto) – Leeor

+0

Dai miei test, salti brevi (1-4 istruzioni) finiscono per essere più veloci in alcune situazioni. – BitBank

4

Potrebbe qualcuno si prega di aiutarmi a capire come cmov migliora ramificazione?

Beh, NON migliora la ramificazione, la rimuove. Un CMOV può essere visto come due istruzioni in uno, un MOV e un NOP. Quale viene eseguito dipende dalle bandiere. Quindi internamente può sembrare

if (cond) { 
    mov dst, src 
} else { 
    nop 
} 

...

Sicuramente il dominio del problema è ancora la stesso- non conosciamo l'indirizzo della prossima istruzione da eseguire?

Bene, no. L'istruzione successiva è sempre quella successiva al CMOV, quindi la pipeline delle istruzioni non viene invalidata e ricaricata (previsione dei rami e altre ottimizzazioni lasciate da parte). È un flusso continuo di macro-opcode.Un semplice esempio sta seguendo

if (ecx==5) 
    eax = TRUE 
else 
    eax = FALSE 

in asm base:

cmp ecx,5  ; is ecx==5 
jne unequal ; what is the address of the next instruction? conditional branch 
mov eax,TRUE ; possibility one 
jmp fin 
unequal:  : possibility two 
mov eax,FALSE 
fin: 
nop 

con cmov

cmp ecx,5 
mov eax, FALSE ; mov doesn't affect flags 
mov ebx, TRUE ; because CMOV doesn't take immediate src operands, use EBX for alternative 
cmove eax, ebx ; executes as MOV if zero-flag is set, otherwise as NOP 
nop    ; always the next instruction, no pipeline stall 

Ne vale la pena su CPU attuali? Un chiaro SÌ. Dalla mia esperienza e (ovviamente) a seconda dell'algoritmo, il guadagno di velocità è significativo e vale la pena.

+0

'cmove' non consente operandi immediati. Quindi devi clobare un registro: 'mov ebx, TRUE' quindi' cmove eax, ebx'. –

+0

Esatto. Oppure usa una variabile come ddTRUE se non hai un registro di riserva. – zx485