2010-03-26 15 views
31

Dire, ho unVelocità di accesso a std :: vector da iteratore vs operatore []/indice?

std::vector<SomeClass *> v; 

nel mio codice e ho bisogno di accedere ai suoi elementi molto spesso nel programma, li loop in avanti e indietro.

Qual è il tipo di accesso più veloce tra questi due?

accesso Iterator:

std::vector<SomeClass *> v; 
std::vector<SomeClass *>::iterator i; 
std::vector<SomeClass *>::reverse_iterator j; 

// i loops forward, j loops backward 
for(i = v.begin(), j = v.rbegin(); i != v.end() && j != v.rend(); i++, j++){ 
    // some operations on v items 
} 

accesso Pedice (in base all'indice)

std::vector<SomeClass *> v; 
unsigned int i, j, size = v.size(); 

// i loops forward, j loops backward 
for(i = 0, j = size - 1; i < size && j >= 0; i++, j--){ 
    // some operations on v items 
} 

E, fa const_iterator offrire un modo più veloce per accedere agli elementi vettoriali in caso io non devo modificarli?

+20

Che cosa hanno mostrato i risultati del profiler? –

+4

Se avessi tempo e voglia di profilare il codice che non avrei chiesto qui. Mi chiedo solo se le implementazioni iterator stl hanno una sorta di ottimizzazione degli accessi. –

+0

Considera l'uso di 'boost :: ptr_vector' se il vettore possiede gli oggetti. Altrimenti usa 'boost :: reference_wrapper'. –

risposta

26

La differenza di prestazioni è probabilmente trascurabile o nessuna (il compilatore potrebbe ottimizzarle per essere identiche); dovresti preoccuparti di altre cose, come se il tuo programma sia corretto (un programma lento ma corretto è meglio di un programma veloce e scorretto). Ci sono altri vantaggi di utilizzare iteratori però, come la possibilità di cambiare il sottostante contenitore di uno senza operator[] senza modificare i loop. Vedi this question per ulteriori informazioni.

const_iterators molto probabilmente non ha nessuna, o trascurabile, differenza di prestazioni rispetto agli iteratori ordinari. Sono progettati per migliorare la correttezza del programma impedendo la modifica di cose che non dovrebbero essere modificate, non per le prestazioni. Lo stesso vale per la parola chiave const in generale.

In breve, l'ottimizzazione non dovrebbe essere una vostra preoccupazione fino a quando due cose sono successe: 1) avete notato che gira troppo lentamente e 2) voi hanno profilato i colli di bottiglia. Per 1), se ha funzionato dieci volte più lentamente di quanto potrebbe, ma viene eseguito sempre una volta e richiede 0,1 ms, a chi importa? Per 2), assicurarsi che sia sicuramente il collo di bottiglia, altrimenti l'ottimizzazione avrà quasi nessun effetto misurabile sulle prestazioni!

+1

Non sono d'accordo. Se l'uso di indici anziché di iteratori ti darà un incremento delle prestazioni, usa solo indici interi. Non stai perdendo nulla usando gli indici, e secondo me sembra davvero più pulito ('for (vector :: iterator iter = list.begin();' vs 'for (int i = 0;') – bobobobo

+0

@bobobobo - ma perdi anche la possibilità di scambiare facilmente container, come ho detto - non puoi usare una lista std :: con un indice intero – AshleysBrain

+0

@AshleysBrain suggerimenti in un buon punto; _ il codice che scrivi non è il codice che è run_. Spesso il compilatore può fare ottimizzazioni del ciclo.Quindi la questione di iteratore vs indice probabilmente non ha importanza dal momento che entrambi genereranno comunque codice di assemblaggio molto simile. – catalyst294

3

ritengo che vettore iteratori vengono implementati come puntatori internamente (in una buona implementazione STL), quindi in generale ci dovrebbe essere differenza di prestazioni trascurabile tra i due idiomi. Ma se vuoi sapere come funzionano su la tua piattaforma, perché non la misuri con un piccolo programma di test? Non penso che ci vorranno più di 5 minuti per misurare il tempo di esecuzione di ad es. 1 milione di iterazioni con entrambe le varianti ...

1

In termini di velocità, penso che potrebbe essere quasi stesso. Meglio, puoi anche profilare e controllare.

almeno si può ridurre il numero delle variabili utilizzate :)

for(i = 0; i < size ; i++){ 
    // some operations on v items 
    v[i]; 
    v[size-i+1]; 
} 

Circa const_iterator: Pls si riferisce il mio D: Un re const_iterators faster ?

+0

sei sicuro che "size - i + 1" per ogni ciclo sia più veloce di un "j--" o meglio di un "--j"? penso di no, quindi preferisco avere più variabili e meno cicli di clock: P –

+0

Immagino si tratti di micro ottimizzazioni e, come si dice, le micro ottimizzazioni sono malvagie! –

+1

@Simone: Penso che sia un'ottimizzazione prematura. Un compilatore potrebbe benissimo generare codice ottimale per l'esempio di Aj. Di nuovo, se non fai il profilo non lo saprai. –

2

Come sempre, dipende. Normalmente non penserei che vedresti alcun tipo di differenza, ma solo tu puoi determinarlo profilando il tuo codice. Alcuni compilatori implementano iteratori vettoriali come puntatori grezzi e altri no. Inoltre, nelle compilazioni di debug, alcuni compilatori potrebbero utilizzare un iteratore verificato, che potrebbe essere più lento. Ma in modalità produzione potrebbe non essere diverso. Guardalo e vedi.

7

Se la velocità conta, allora dovresti avere il tempo e la volontà di eseguire un profiler su di esso e vedere quale funziona meglio nel tuo caso.

Se non è importante, si sta eseguendo un'ottimizzazione prematura e si dovrebbe smettere di farlo.

+8

Questo non risolve veramente la domanda. – NargothBond

1

Non si sta solo ottimizzando prematuramente, si sta ottimizzando in micro. Questo è un male quasi peggiore del primo (la differenza è che molto, molto, molto raramente è effettivamente necessario ottimizzare il micro). Metti insieme i due e hai una ricetta per il disastro.

Se si esegue un profiler e si trova questa area di codice è un collo di bottiglia, quindi sarà necessario ottimizzare. Non ottimizzi cercando di ridurre il tuo ciclo dall'assunzione di 23 cicli di clock a 22. Ottimizza trovando i modi per ridurre O() del tuo algoritmo. Ma fino a quando non eseguirai un profiler dovresti prestare maggiore attenzione al design rispetto a qualsiasi altra preoccupazione.

1

Mi piacerebbe andare agli iteratori, ma quello che ottimizzerei è chiamare end() nel ciclo e cambierebbe il preincremento in postincremento. Cioè Avrei

std::vector<SomeClass *> v; 
std::vector<SomeClass *>::iterator i,ie; 
std::vector<SomeClass *>::reverse_iterator j,je; 

// i loops forward, j loops backward 
for(i=v.begin(),ie=v.end(), j=v.rbegin(),je=v.rend(); i!=ie && j!=je; ++i,++j){ 
    // some operations on v items 
} 

E non penso che sia una microottimizzazione prematura, è solo scrivere un codice migliore. Molto meno malvagio di ogni tentativo di scrivere codice efficiente prematuro di microottimizzazione e sostituzione del pensiero con il profiling.

+0

E perché non rimuovere 'j! = Je' nel test, in quanto le due condizioni sono identiche? – rafak

+0

rafak, le condizioni non sono identiche, anche se dovrebbero coincidere. Ho solo conservato la logica originale, che è in qualche modo valida - non c'è niente di sbagliato nell'essere al sicuro. È improbabile che mantenga entrambe le condizioni, però, nel mio codice. –

1

ero confuso su qualcosa di simile e ha scritto un programma per testare le prestazioni: https://github.com/rajatkhanduja/Benchmarks/blob/master/C%2B%2B/vectorVsArray.cpp

Ecco le osservazioni pertinenti per la lettura/scrittura di vettore <int> di dimensioni 1m con g ++ (senza flag di ottimizzazione), sulla Linux-i686 (macchina a 64 bit) con 7,7 GB di RAM: -

Tempo impiegato per scrivere sul vettore utilizzando gli indici. : 11.3909 ms

Tempo impiegato per leggere dal vettore utilizzando gli indici, in sequenza. : 4.09106 ms

Tempo impiegato per leggere dal vettore utilizzando gli indici, in modo casuale. : 39 ms

Tempo impiegato per scrivere sul vettore utilizzando gli iteratori (in sequenza). : 24.9949 ms

Tempo impiegato per leggere dal vettore utilizzando gli iteratori (in sequenza). : 18.8049 ms

+3

Cosa sono le bandiere del compilatore? 'for (int i = 0; i Ponkadoodle

1

Con l'ottimizzazione (-O2) i tempi dovrebbero migliorare (dovrebbe essere quasi identico).

12

Un semplice benchmark basato su loop è stato soddisfatto. Ho usato VS 2010 SP1 (configurazione di rilascio).

  1. Usa iteratori (* it = * it + 1;)
  2. usare indici (vs [i] = vs [i] + 1;)

In diversi miliardi di iterazioni seconda l'approccio si è rivelato un po 'più veloce, dell'1%. Il risultato (gli indici sono leggermente più veloci degli iteratori) è riproducibile ma la differenza, come ho detto, è molto piccola.

1

Ho fatto un test ieri, uso [] vs iteratore, il codice è creare un vettore con alcuni elementi e rimuovere alcuni elementi dal vettore. Questo è il codice utilizza operatore [] per accedere agli elementi

TimeSpent([](){ 
    std::vector<int> vt = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; 
    for (int i = int(vt.size()) - 1; i >= 0; i--) 
    { 
     if (vt[i] % 2 == 0) 
     { 
     //cout << "removing " << vt[i] << endl; 
     vt.erase(vt.begin() + i); 
     } 
    } 
    }); 

Il codice seguente è la frazione elementi accesso vettore utilizzando iteratore

TimeSpent([](){ 
    std::vector<int> vt = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; 
    for (std::vector<int>::iterator num = vt.begin(); num != vt.end();) 
    { 
     if (*num % 2 == 0) 
     { 
     num = vt.erase(num); 
     } 
     else 
     { 
     ++num; 
     } 
    } 
    }); 

testato chiamandoli da questa funzione separatamente

void TimeSpent(std::function<void()> func) 
{ 
    const int ONE_MIL = 10000; 
    long times = ONE_MIL; 
    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); 
    while (times > 0) 
    { 
    func(); 
    --times; 
    } 
    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); 
    cout << "time elapsed : " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << endl; 
} 


Ambiente testato è Visual Studio 2013 pro. versione 4.5.51650
I risultati sono:
operator []: 192
iteratore: 212
Riassunto: quando si accede al contenitore vettore, operatore [] è più veloce di iteratore.

Problemi correlati