2013-03-21 21 views
14

Un modello molto comune nella programmazione è quello di limitare un valore al massimo dopo un qualche tipo di aggiornamento. Quello che mi piacerebbe sapere, è se c'è una differenza tra i due pezzi di codice, e se uno dovrebbe essere preferito:In C++, è meglio limitare un valore usando std :: min o un ramo if?

value += increment; 
value = std::min(value, valueMax); 

vs

value += increment; 

if (value > valueMax) 
    value = valueMax; 

Il mio pensiero è che questo si riduce se le CPU hanno istruzioni per prendere due valori e produrre il minimo. In tal caso, la chiamata a std :: min dovrebbe risultare in questa istruzione ed evitare un ramo non necessario. In caso contrario, la seconda versione evita un'assegnazione non necessaria quando il valore è < = valueMax.

Non sono molto bravo con questo genere di cose, ma sono sicuro che ci siano gli hacker della vecchia scuola che lo saprebbero. A loro chiedo: che è meglio?

+2

Provate entrambi e guardate all'assemblaggio ... – Mysticial

+0

Direi che la prima versione eseguirà sempre * almeno * e la seconda versione, quindi non c'è ragione di non usarlo. La prima versione potrebbe * anche * essere più veloce, anche se non ci sono garanzie al riguardo. –

+0

Come implicito in Mysticial, dipende dall'implementazione di std :: min (http://en.cppreference.com/w/cpp/algorithm/min). – DavidA

risposta

10

I compilatori moderni sono abbastanza intelligenti da generare lo stesso codice in entrambi i casi. Ad esempio, a 32 bit GCC genera:

addl %esi, %edi 
cmpl %edx, %edi 
movl %edi, %eax 
cmovgl %edx, %eax 

64-bit Clang:

%1 = add nsw i32 %increment, %value 
%2 = icmp sgt i32 %1, %valueMax 
%value = select i1 %2, i32 %valueMax, i32 %1 
+0

+1. Potresti dare alcune annotazioni all'assemblea qui? C'è un ramo? – voltrevo

+2

@ Mozza314 Non ci sono filiali qui. Il compilatore ha fatto il meglio che si può fare - che è usare una mossa condizionale. – Mysticial

4

Su VC10 in uscita per il seguente codice abbiamo la seguente assemblea:

int main(int argc, char *argv[]) 
{ 
    int dummyValue = 0, valueMax = 3000, value = valueMax + 1; 

    cin >> valueMax; 
    cin >> value; 

    dummyValue = std::min(value, valueMax); 

    cout << dummyValue; 
    cin >> valueMax; 
    cin >> value; 

    if (value > valueMax) 
    dummyValue = valueMax; 

    cout << dummyValue; 
    return 0; 
} 

Generated :

24: dummyValue = std::min(value, valueMax); 
00E112AF mov   eax,dword ptr [valueMax] 
00E112B2 cmp   eax,dword ptr [value] 
00E112B5 lea   edx,[value] 
00E112B8 lea   ecx,[valueMax] 
00E112BB cmovge  ecx,edx  // <-- this is our conditional assignment 
00E112BE mov   esi,dword ptr [ecx] 

e

if (value > valueMax) 
    dummyValue = valueMax 
00E112ED mov   eax,dword ptr [valueMax] 
00E112F0 cmp   dword ptr [value],eax 
00E112F3 mov   ecx,dword ptr ds:[0E13038h] 
00E112F9 cmovg  esi,eax 

Quindi entrambi i casi ottimizzati per entrambi cmovge o cmovg istruzioni.

Vorrei ancora andare con std::min perché mostra intento migliore di una dichiarazione if. È ottimizzato ed è più leggibile.

+0

Quando si forniscono valori costanti alle variabili per algoritmi semplici come min (che è in linea la maggior parte delle volte). Il compilatore può calcolare, al momento della compilazione, il valore minimo. Il ciclo non verrà eseguito, quindi otterrai '0'. Prova a generare valori passati a min in fase di esecuzione e otterrai la misurazione corretta. –

+0

In ogni caso, il test è un po 'rotto in diversi modi. 1) In effetti non fa nulla. Quindi il compilatore è libero di ottimizzare entrambi i loop. Questo può essere risolto usando il risultato dei loop. 2) L'intero primo ciclo sarebbe ottimizzato per 'dummyValue = 3001'. E l'intero secondo ciclo sarebbe ottimizzato per 'value = 3001'. – Mysticial

0

La risposta dipende dal tipo di valore. Il codice potrebbe essere efficacemente ottimizzato se tutte le operazioni sono completamente trasparenti per il programma di ottimizzazione del codice, il che sarebbe il caso se il valore è un numero intero semplice. Ma il tuo codice verrebbe compilato anche se value fosse una std :: string, e quindi la seconda versione potrebbe essere potenzialmente più veloce poiché il compito è condizionale.