2010-03-30 10 views
7

Se abbiamo i seguenti 2 frammenti di codice in C++ che fanno lo stesso compito:cos'è più veloce?

int a, b=somenumber; 
while(b > 0) 
{ 
a = b % 3; 
b /= 3; 
} 

o

int b=somenumber; 
while(b > 0) 
{ 
int a=b%3; 
b /= 3; 
} 

io non ne so molto di computer di architettura/C++ di progettazione, ma penso che il primo codice è più veloce perché dichiara l'intero a all'inizio e lo usa solo nel ciclo while, e nel secondo codice l'intero a viene dichiarato ogni volta che il ciclo while ricomincia. Qualcuno può aiutarmi con questo, sono corretto o cosa e perché?

+4

Perché non cronometra entrambe le soluzioni e vedi tu stesso? –

+35

Se c'è qualche differenza, allora hai bisogno di un nuovo compilatore. –

+2

non sono equivalenti. a è circoscritto al ciclo while nel secondo ma allo scope che racchiude nel primo. –

risposta

11

La dichiarazione int è un'informazione per il compilatore e non traduce in un'istruzione che deve essere codificata. Quindi non fa differenza. Dichiarare l'int all'interno del ciclo non diminuirà il ciclo. Perché non provare a compilare entrambi per te e ottenere il compilatore per l'output del codice assembly in modo da poter vedere da soli.

-6

Il primo DOVREBBE essere più veloce; tuttavia il compilatore di solito è abbastanza intelligente da ottimizzarlo, quindi probabilmente non ha importanza.

Per purezze bene però, la risposta è il primo

EDIT: E 'più veloce perché richiede solo un'allocazione rispetto a N (N è il numero di iterazioni eseguirà il ciclo while).

+1

AFAIK questo è sbagliato. Tutte le variabili locali sono allocate allo stack una sola volta per chiamata di funzione. –

+2

Non sono sicuro che sia corretto. Il 'a' locale non è usato nel secondo esempio, quindi il compilatore può tranquillamente liberarsene. E in ogni caso mi aspetterei che riutilizzi lo stesso spazio di stack per ogni iterazione. –

+1

Non c'è allocazione nel ciclo e "inizializzazione" (assegnazione) viene eseguita in entrambi i casi (o in nessuno dei due, a causa del fatto che il compilatore è intelligente). –

1

No, non può essere "dichiarato" nel ciclo, come dichiarato in fase di compilazione. Direi che sono uguali, ma il secondo potrebbe essere più veloce se il tipo di variabile fosse qualcosa di più complicato, con costruttore e distruttore.

5

Seriamente, è davvero importante? Questo è il tipo di micro-ottimizzazioni che dovresti cercare di evitare. Scrivi il codice che è più leggibile quale IMHO è il secondo ciclo. Il compilatore è abbastanza buono da fare l'ottimizzazione per questo tipo di cose e vorrei lasciarlo fare.

+0

Non mi dispiace il voto negativo, ma una ragione sarebbe buona :-) – Naveen

+1

Non è ovvio? Hai persino provato a rispondere alla domanda? ;-) –

+0

Il suo punto è; è "più veloce" non preoccuparsi di questa micro-ottimizzazione che cercare effettivamente di ottimizzarla: così sicuramente il primo codice proposto è più veloce, perché l'OP pensa che sia il più veloce. – Pindatjuh

2

Non c'è "più veloce" nello standard C++, ad eccezione delle garanzie di prestazioni nella libreria standard. Un compilatore ottimizzante probabilmente eliminerebbe solo lo a, dal momento che non è utilizzato. In alternativa, potrebbe allocare tutta la memoria necessaria per tutte le variabili locali contemporaneamente, e quindi non farebbe alcuna differenza.

L'unica domanda legittima sui costrutti di linguaggio di basso livello come questo è se la particolare implementazione li esegue più velocemente o più lentamente e il modo migliore per scoprirlo è cronometrare autonomamente. Scoprirai che molte di queste cose semplicemente non contano, e se esamini il codice generato troverai spesso che i compilatori fanno la stessa cosa con diversi modi di scrivere codice.

In genere, cercare le micro-ottimizzazioni è una cattiva idea, ma se si sta tentando di impostare uno stile generale potrebbe valerne la pena (utilizzando ++i anziché i++, ad esempio). Tuttavia, se stai impostando uno stile per scopi diversi dalla leggibilità, dovresti avere buoni motivi per farlo. In questo caso, ciò significa test per le prestazioni.

+0

Quello che avrei detto, ma molto più eloquente di quanto potrei mai essere. –

1

In teoria la prima opzione potrebbe essere più veloce. In pratica, mi aspetterei che a e b venissero inseriti nei registri in modo tale che l'assemblaggio generato risultasse identico (che è possibile verificare nel binario compilato). Se stai eseguendo il ciclo abbastanza volte che pensi che ci possa essere una differenza, l'unico modo per sapere è misurare. Se il tuo profiler non può distinguere un altro, codificalo nel modo che rende il codice il più chiaro ai futuri manutentori.

In generale (come già accennato) questi tipi di ottimizzazioni non forniranno alcun miglioramento significativo delle prestazioni del programma. Dovresti invece cercare ottimizzazioni algoritmiche e di design.

14

Non ci dovrebbero essere differenze, ma per essere extra empirico (anale?) Ho provato questo con g ++, creando una funzione per ciascuno dei frammenti di codice. Sia con che senza ottimizzazioni ha generato il codice identico, indipendentemente da dove si trovi la dichiarazione int a.

#include <iostream> 

int variant_a(int b) 
{ 
     int a; 
     while(b > 0) 
     { 
       a = b % 3; 
       b /= 3; 
     } 
     return b; 
} 

int variant_b(int b) 
{ 
     while(b > 0) 
     { 
       int a = b % 3; 
       b /= 3; 
     } 
     return b; 
} 

int main() 
{ 
     std::cout << variant_a(42) << std::endl; 
     std::cout << variant_b(42) << std::endl; 
} 

Questo è il ciclo non ottimizzata:

_Z9variant_ai: 
.LFB952: 
     pushl %ebp 
.LCFI0: 
     movl %esp, %ebp 
.LCFI1: 
     subl $24, %esp 
.LCFI2: 
     jmp  .L2 
.L3: 
     movl 8(%ebp), %eax 
     movl %eax, -20(%ebp) 
     movl $1431655766, -24(%ebp) 
     movl -24(%ebp), %eax 
     imull -20(%ebp) 
     movl %edx, %ecx 
     movl -20(%ebp), %eax 
     sarl $31, %eax 
     subl %eax, %ecx 
     movl %ecx, %eax 
     addl %eax, %eax 
     addl %ecx, %eax 
     movl -20(%ebp), %edx 
     subl %eax, %edx 
     movl %edx, %eax 
     movl %eax, -4(%ebp) 
     movl 8(%ebp), %eax 
     movl %eax, -20(%ebp) 
     movl $1431655766, -24(%ebp) 
     movl -24(%ebp), %eax 
     imull -20(%ebp) 
     movl %edx, %ecx 
     movl -20(%ebp), %eax 
     sarl $31, %eax 
     movl %ecx, %edx 
     subl %eax, %edx 
     movl %edx, %eax 
     movl %eax, 8(%ebp) 
.L2: 
     cmpl $0, 8(%ebp) 
     jg  .L3 
     movl 8(%ebp), %eax 
     leave 
     ret 

e ottimizzato uno:

_Z9variant_ai: 
.LFB968: 
     pushl %ebp 
.LCFI0: 
     movl %esp, %ebp 
.LCFI1: 
     pushl %ebx 
.LCFI2: 
     movl 8(%ebp), %ebx 
     testl %ebx, %ebx 
     jle  .L2 
     movl $1431655766, %ecx 
     .p2align 4,,7 
     .p2align 3 
.L5: 
     movl %ebx, %eax 
     imull %ecx 
     movl %ebx, %eax 
     sarl $31, %eax 
     movl %edx, %ebx 
     subl %eax, %ebx 
     jne  .L5 
.L2: 
     movl %ebx, %eax 
     popl %ebx 
     popl %ebp 
     ret 
+0

Almeno qualcuno ha effettivamente eseguito il backup del reclamo con i dati. Non che io abbia davvero dubitato, ma preferisco i fatti alle supposizioni. –

+3

Errr ... Spero che tu abbia modificato questo post dopo aver premuto il pulsante "Aggiungi commento". (Suggerimento: variant_a e variant_b sono identici?) – Pindatjuh

+3

Le tue due funzioni aeb sono uguali, non dovrebbero essere diverse? –

0

Non credo ci sarebbe alcuna differenza nella pratica. Non ci sono allocazioni di memoria coinvolte, perché la memoria per le variabili automatiche è allocata o messa da parte in fase di compilazione.

In teoria penso che il secondo potrebbe essere anche più veloce: il compilatore ha più informazioni su dove e come vengono utilizzate le variabili (ad esempio potresti riutilizzare la stessa variabile per qualcosa di completamente non correlato in seguito).

Si potrebbe iniziare a preoccuparsi di cose del genere quando si ha a che fare con tipi che sono costosi da costruire. E. Devo dichiarare un vettore std :: nel ciclo interno, o dovrei dichiararlo prima del ciclo e clear() all'inizio del corpo del ciclo (riutilizzando la memoria allocata).