2010-10-21 17 views
7

Ho una domanda riguardante la velocità di dereferenziazione del puntatore. Ho una struttura in questo modo:Velocità di dereferenziamento puntatore di struttura C

typedef struct _TD_RECT TD_RECT; 
struct _TD_RECT { 
    double left; 
    double top; 
    double right; 
    double bottom; 
}; 

La mia domanda è, quale di questi sarebbe più veloce e perché?


CASO 1:

TD_RECT *pRect; 
... 
for(i = 0; i < m; i++) 
{ 
    if(p[i].x < pRect->left) ... 
    if(p[i].x > pRect->right) ... 
    if(p[i].y < pRect->top) ... 
    if(p[i].y > pRect->bottom) ... 
} 

CASO 2:

TD_RECT *pRect; 
double left = pRect->left; 
double top = pRect->top; 
double right = pRect->right; 
double bottom = pRect->bottom; 
... 
for(i = 0; i < m; i++) 
{ 
    if(p[i].x < left) ... 
    if(p[i].x > right) ... 
    if(p[i].y < top) ... 
    if(p[i].y > bottom) ... 
} 

Quindi, nel caso 1, il ciclo viene dereferenziazione direttamente il puntatore pRect avere la confronto valori. Nel caso 2, sono stati fatti nuovi valori sullo spazio locale della funzione (nello stack) ei valori sono stati copiati da pRect alle variabili locali. Attraverso un ciclo ci saranno molti confronti.

Nella mia mente, sarebbero ugualmente lento, perché la variabile locale è anche un punto di riferimento della memoria sullo stack, ma non sono sicuro ...

Inoltre, sarebbe meglio tenere riferimento p [] per indice, o incrementa p di un elemento e dereferenza direttamente senza un indice.

Qualche idea? Grazie :)

+13

Smetti di sprecare il tuo tempo con l'ottimizzazione prematura che molto probabilmente non farà una piccola differenza. –

+1

forse la frazione di uno smidge conta, ma se lo fa, perché non misurarlo? – kenny

+0

Per Win32, potrei usare GetTickCount() per misurare il tempo prima e dopo aver chiamato il loop per misurare la velocità, o c'è un modo migliore? – oldSkool

risposta

1

Penso che il secondo caso sarà probabilmente più veloce perché non stai dereferenziando il puntatore a pRect su ogni iterazione del ciclo.

In pratica, un compilatore che esegue l'ottimizzazione potrebbe notare ciò e non potrebbe esserci differenza nel codice generato, ma la possibilità che pRect sia un alias di un elemento in p [] potrebbe impedirlo.

12

Probabilmente troverete che non farà la differenza con i moderni compilatori. La maggior parte di essi probabilmente eseguirà l'eliminazione di sottoespressione comune delle espressioni che non cambiano all'interno del ciclo. Non è saggio presumere che esista una semplice mappatura uno a uno tra le tue istruzioni C e il codice assembly. Ho visto gcc pump out codice che avrebbe messo vergogna le mie capacità di assemblatore.

Ma questa non è una domanda C o C++ poiché lo standard ISO non impone come è fatto. Il modo migliore per verificare è generare il codice assembler con qualcosa come gcc -S ed esaminare i due casi in dettaglio.

Otterrete anche più ritorno sull'investimento se vi allontanate da questo tipo di micro-ottimizzazione e vi concentrate maggiormente sul livello macro, come la selezione dell'algoritmo e così via.

E, come con tutte le domande di ottimizzazione, misura , non indovinare! Ci sono troppe variabili che possono influire su di esso, quindi è necessario eseguire il benchmarking di approcci differenti nell'ambiente di destinazione e con dati realistici.

+0

Mi sto chiedendo perché la funzione che sto scrivendo è il ritaglio di poligoni per le mappe vettoriali che contengono milioni dei vertici ... qualsiasi velocità che posso spremere mi aiuterà perché ho bisogno di ritagliare ogni sezione in aree di 1 grado. – oldSkool

+2

Va bene. La cosa corretta da fare è eseguire benchmark del mondo reale poiché dipende da un gran numero di fattori, molti dei quali lo sappiamo. Come minimo, la macchina target, il compilatore, la CPU, altri problemi di architettura come sottosistemi di memoria e I/O, la composizione dei dati, il livello di ottimizzazione e così via. – paxdiablo

+0

Che ne hai milioni, guarda il mio commento qui sotto. Crea un indice sulla tua mappa vettoriale (cioè p?), Ordinato per xe per y. (Rimane abbastanza statico?). Oppure ordinalo su x e hai un indice su y. Usa la ricerca binaria per trovare tutti x a destra, tutti y in basso. Quindi, se hai detto 4 milioni di 22 confronti per ciascuno, 88 in totale, invece dei 16 milioni che hai ora! – CashCow

3

Non è probabile che si tratti di una differenza enorme in termini di prestazioni. Si potrebbe profilare l'esecuzione di ciascuna opzione più volte e vedere. Assicurati di aver impostato le ottimizzazioni del compilatore nel test.

Per quanto riguarda la memorizzazione dei doppi, è possibile ottenere un risultato di prestazioni utilizzando const. Quanto è grande il tuo array?

Per quanto riguarda l'uso dell'aritmetica del puntatore, questo può essere più veloce, sì.

È possibile ottimizzare immediatamente se si conosce lasciato < proprio nel tuo rect (sicuramente deve essere). Se x < lasciato non può essere> corretto in modo da poter inserire un "altro".

La tua grande ottimizzazione, se ce n'è una, verrebbe dal non dover scorrere tutti gli elementi dell'array e non dover eseguire 4 controlli su tutti loro.

Ad esempio, se hai indicizzato o ordinato il tuo array su xey, saresti in grado, utilizzando la ricerca binaria, di trovare tutti i valori che hanno x < lasciato e passare proprio attraverso quelli.

0

Sarò sorpreso se anche una compilazione totalmente non ottimizzata (- O0) produrrà un codice diverso per i due casi presentati. Per eseguire qualsiasi operazione su un processore moderno, i dati devono essere caricati nei registri. Quindi anche quando si dichiarano variabili automatiche, queste variabili non saranno presenti nella memoria principale ma in uno dei registri a virgola mobile del processore. Questo sarà vero anche quando non dichiarate le variabili da soli e quindi non mi aspetto alcuna differenza nel codice macchina generato anche per quando dichiarate le variabili temporanee nel vostro codice C++.

Ma come altri hanno già detto, compilare il codice nell'assemblatore e vedere di persona.

Problemi correlati