2009-08-25 9 views
9

dire che ho uno struct come il seguente ...C puntatori vs accesso membro diretto per le strutture

typedef struct { 
    int WheelCount; 
    double MaxSpeed; 
} Vehicle; 

... e ho una variabile globale di questo tipo (io sono ben consapevole delle insidie di globals, questo è per un sistema embedded, che non ho progettato, e per il quale sono un male sfortunato ma necessario.) È più veloce accedere ai membri della struct direttamente o tramite un puntatore? cioè

double LocalSpeed = MyGlobal.MaxSpeed; 

o

double LocalSpeed = pMyGlobal->MaxSpeed; 

Uno dei miei compiti è quello di semplificare e fissare un sistema embedded di recente ereditato.

+1

Che cosa fornisce un puntatore a quell'accesso diretto no? E un puntatore è più lento. –

+3

-1 per chiedere informazioni sulla micro-ottimizzazione senza prima averla analizzata. – bk1e

risposta

19

In generale, direi che andare con la prima opzione:

double LocalSpeed = MyGlobal.MaxSpeed; 

Questo ha un dereferenziare meno (non sta trovando il puntatore, quindi dereferenziazione esso per ottenere alla sua posizione). È anche più semplice e più facile da leggere e gestire, poiché non è necessario creare la variabile puntatore oltre alla struct.

Detto questo, non penso che le differenze di prestazioni che si vedrebbero sarebbero evidenti, anche su un sistema embedded. Entrambi saranno tempi di accesso molto, molto veloci.

+0

Perché i downvotes? Sarei curioso di sapere perché le persone non sono d'accordo. –

+1

Sono con Reed qui. Il riferimento diretto è più sicuro, non richiede un controllo NULL e salva un'operazione di dereferenziazione. È necessario accedere comunque al contenuto della memoria della variabile, quindi perché eseguire una ricerca aggiuntiva. Un puntatore non ti guadagna nulla se hai già accesso diretto alla variabile. –

+0

Per quanto riguarda il dereferenziamento del puntatore, questo è quello che pensavo, è passato molto tempo da quando ho avuto a che fare con C o C++. Grazie ragazzi per il vostro consiglio. –

1

Suppongo che, se questo fa davvero la differenza, ciò dipenderebbe dall'architettura.

1

In C, non ci dovrebbero essere differenze o un colpo di performance insignificante.

studenti C sono insegnati:

pMyGlobal->MaxSpeed == (*pMyGlobal).MaxSpeed 

si dovrebbe essere in grado di confrontare lo smontaggio di loro sia per convincerti che sono essenzialmente lo stesso, anche se non sei un programmatore Assembly-codice.

Se stai cercando un'ottimizzazione delle prestazioni, guarderei altrove. Non sarai in grado di risparmiare abbastanza cicli di CPU con questo tipo di micro-ottimizzazione.

Per motivi di stile, preferisco la notazione Struttura-Punto, specialmente quando si tratta di singleton-globals. Lo trovo molto più pulito da leggere.

+1

La domanda non riguarda -> vs *, si tratta di utilizzare un puntatore in primo luogo. –

+1

La domanda è: "È più veloce accedere ai membri della struct direttamente o tramite un puntatore?" Credo di aver risposto alla domanda. – abelenky

8

Il primo deve essere più veloce poiché non richiede il dereferenziamento del puntatore. Poi di nuovo questo è vero per i sistemi basati su x86, non sicuro per gli altri.

su x86 il primo si tradurrebbe in qualcosa di simile

mov eax, [address of MyGlobal.MaxSpeed] 

e la seconda sarebbe qualcosa di simile

mov ebx, [address of pMyGlobal] 
mov eax, [ebx+sizeof(int)] 
1

In generale, accedendo alla struct direttamente sarebbe stato più veloce, come non richiederà un dereferenziamento puntatore aggiuntivo. Il puntatore di distorsione significa che deve prendere il puntatore (la cosa nella variabile), caricare qualsiasi cosa a cui punta, quindi operare su di esso.

3

Sulla piattaforma integrata, è probabile che l'architettura sia ottimizzata in modo tale che sia essenzialmente un lavaggio, e anche se non lo fosse si noterebbe solo un impatto sulle prestazioni se questo è stato eseguito in un ciclo molto stretto .

Probabilmente esistono aree di prestazioni del sistema molto più evidenti.

3
struct dataStruct 
{ 
    double first; 
    double second; 
} data; 

int main() 
{ 
    dataStruct* pData = &data; 

    data.first = 9.0; 
    pData->second = 10.0; 
} 

Questa è la modalità di rilascio VS2008 uscita assembly utilizzando:

data.first = 9.0; 
008D1000 fld   qword ptr [[email protected] (8D20F0h)] 

    pData->second = 10.0; 
008D1006 xor   eax,eax 
008D1008 fstp  qword ptr [data (8D3378h)] 
008D100E fld   qword ptr [[email protected] (8D20E8h)] 
008D1014 fstp  qword ptr [data+8 (8D3380h)] 
+1

Ora scegli come target la sua piattaforma integrata. – Alan

+0

quali erano le impostazioni di ottimizzazione? –

+0

Questo è sbagliato. Il tuo primo membro vincerà sempre per definizione di una struct (il puntatore di una struct è anche un puntatore di un primo membro). Dovresti confrontare secondo vs secondo. –

2

smontare, smontare, smontare ...

A seconda delle linee di codice non si sta mostrando noi è possibile che se il tuo puntatore è statico un buon compilatore lo saprà e pre-calcolerà l'indirizzo per entrambi. Se non ci sono ottimizzazioni allora tutta questa discussione è muta. Dipende anche dal processore che si sta utilizzando, entrambi possono essere eseguiti con una singola istruzione a seconda del processore. Così ho seguito le fasi di ottimizzazione di base:

1) smontare ed esaminare 2) tempo di esecuzione

Come accennato in precedenza anche se la linea di fondo è che può essere un caso di due istruzioni invece di uno che costa un singolo orologio ciclo che probabilmente non vedresti mai. La qualità delle scelte del compilatore e dell'ottimizzatore produrrà differenze di prestazioni molto più drammatiche rispetto al tentativo di modificare una riga di codice nella speranza di migliorare le prestazioni. Il cambio di compilatore può darti il ​​10-20% in entrambe le direzioni, a volte di più. Come può cambiare i tuoi flag di ottimizzazione, l'attivazione di tutto non fa il codice più veloce, a volte -O1 si comporta meglio di -O3.

Comprendere cosa producono queste due righe di codice e come ottimizzare le prestazioni dal linguaggio di alto livello deriva dalla compilazione per diversi processori e dal disassemblaggio utilizzando vari compilatori. E ancora più importante il codice attorno alle linee in questione gioca un ruolo importante nel modo in cui il compilatore ottimizza quel segmento.

Utilizzando esempio di qualcun altro su questa questione:

typedef struct 
{ 
    unsigned int first; 
    unsigned int second; 
} dataStruct; 

dataStruct data; 

int main() 
{ 
    dataStruct *pData = &data; 

    data.first = 9; 
    pData->second = 10; 

    return(0); 
} 

con GCC (non un gran che un compilatore) si ottiene:

mov r2, #10 
mov r1, #9 
stmia r3, {r1, r2} 

Così entrambe le righe di codice C sono uniti in un unico archivio, il problema qui è l'esempio usato come test. Due funzioni separate sarebbero andate un po 'meglio ma ha bisogno di molto più codice attorno ad esso e il puntatore deve puntare ad un'altra memoria in modo che l'ottimizzatore non si accorga che è un indirizzo globale statico, per testare questo è necessario passare l'indirizzo in quindi il compilatore (bene gcc) non riesce a capire che si tratta di un indirizzo statico.

O senza ottimizzazioni, stesso codice, stesso compilatore, nessuna differenza tra puntatore e diretto.

mov r3, #9 
str r3, [r2, #0] 

mov r3, #10 
str r3, [r2, #4] 

Questo è ciò che ci si aspetterebbe di vedere a seconda del compilatore e del processore, potrebbe non esserci differenza. Per questo processore, anche se il codice di test nascondesse l'indirizzo statico del puntatore dalla funzione, sarebbe comunque ridotto a due istruzioni. Se il valore memorizzato nell'elemento della struttura fosse già stato caricato in un registro, sarebbe un'istruzione in entrambi i casi, puntatore o diretto.

Quindi la risposta alla tua domanda non è assoluta ...dipende. smontare e testare.

+0

utilizzando double invece di ints significa solo più registri il negozio stesso è ancora una singola istruzione per il puntatore o l'accesso diretto, quindi non c'è ancora alcuna differenza, per questo compilatore per questo processore per questa sessione di compilazione. –

+0

Questo è sbagliato. Il tuo primo membro vincerà sempre per definizione di una struct (il puntatore di una struct è anche un puntatore di un primo membro). Dovresti confrontare secondo vs secondo. –

0

L'accesso diretto ai membri è più veloce (per i puntatori si otterrebbe di solito un'operazione di deferenziazione del puntatore in genere). Anche se mi sto divertendo a immaginarlo in una situazione in cui sarebbe un problema, performance o altro.

Problemi correlati