2010-06-21 3 views
14

In generale, è una buona idea memorizzare nella cache un iteratore finale (in particolare i contenitori STL) per motivi di efficienza e velocità? come nel seguente bit di codice:Memorizzazione nella cache dell'iter iter - Buona idea o cattiva idea?

std::vector<int> vint; 
const std::vector<int>::const_iterator end = vint.end(); 
std::vector<int>::iterator it = vint.begin(); 

while (it != end) 
{ 
    .... 
    ++it; 
} 

In quali condizioni il valore finale sarebbe stato invalidato? cancellerebbe dal contenitore causa end per essere invalidato in tutti contenitori STL o solo alcuni?

+1

Prima chiediti, il tuo profiler ti dice che la chiamata a std :: vector :: end() costa una quantità significativa di tempo di elaborazione? – daramarak

risposta

14

Nel caso semplice di vector, l'iteratore end cambia quando si aggiungono o si rimuovono elementi dal contenitore; tuttavia, di solito è più sicuro presumere che se si muta il contenitore mentre si itera su di esso, gli di tutti gli iteratori dello non sono più validi. Gli iteratori possono essere implementati in modo diverso in ogni data implementazione STL.

Per quanto riguarda la memorizzazione nella cache dell'iteratore end - è sicuramente valido per memorizzarlo nella cache, ma per scoprire se è effettivamente più veloce nel tuo caso, la soluzione migliore è quella di profilare il codice e vedere.Mentre il recupero dell'iter end da un vector è probabilmente un'implementazione rapida con una libreria e un compilatore STL recenti, I ha funzionato su progetti precedenti in cui il caching dell'iteratore end ci ha dato un notevole aumento di velocità. (Questo era su PlayStation 2, quindi prendi con un pizzico di sale.)

2

Cancellare da un contenitore su cui si sta attualmente iterando è sempre una cattiva idea. La memorizzazione nella cache effettiva del tuo iteratore finale non cambierà questo.

h.

+5

Niente affatto, se lo fai correttamente. Il membro 'cancella' di std :: vector, ad esempio, restituisce un nuovo iteratore appropriato. Quindi, se scrivi il tuo codice correttamente (ovvero, ricorda che gli altri iteratori ora non sono validi) non avrai problemi a cancellare da un contenitore su cui stai iterando. –

4

Se parliamo di efficienza e velocità: il caching dell'iter iter finale non è necessario a causa dell'ottimizzazione del compilatore e dell'inlinazione.

+7

Davvero? La maggior parte dei consigli che ho visto predicare per l'uso degli algoritmi STL (come 'for_each') erano che memorizzerebbe nella cache l'iteratore' end' in modo che non venisse calcolato ad ogni iterazione. –

+0

@Matthieu: gli iteratori non sono garantiti per sopravvivere alla modifica di alcuni contenitori (ad esempio il vettore di ridimensionamento invalida * tutti * gli iteratori sul vettore). for_each non fornisce l'accesso all'oggetto funzione all'iteratore, il contenitore non può essere modificato, quindi è OK memorizzare nella cache l'iteratore. ma se si ingannano i sistemi e dall'oggetto function si modifica il contenitore in modo tale da invalidare gli iteratori, allora ovviamente si otterrebbe * un comportamento non definito *, probabilmente in crash, quando si accede al contenitore sull'iteratore ora non valido. – Dummy00001

+0

In realtà, il ridimensionamento di un vettore invalida solo gli iteratori se si verifica una riallocazione e puoi assicurarti che non lo faccia se ridimensioni solo fino alla capacità. Ma il punto della mia domanda rimane, non sono sicuro che il compilatore sia in grado di ottimizzare il calcolo di 'end()' out of the loop, anche se inline il compilatore come a priori non c'è modo di sapere se il valore è probabile che arrivi modificato dalle operazioni all'interno del ciclo per quanto ne so. Ma poi non so molto delle ottimizzazioni del compilatore, quindi la mia domanda :) –

1

Io uso spesso questo stile per contenitori iterazione:

// typedef std::vector<Person> Persons; 
Persons::iterator it = persons.begin(), end = persons.end(); 
for (; it != end; ++it) 
{ 
    Person & person = *it; 
    // ... 
} 

Cancellazione di un elemento da un vettore invalida tutti gli iteratori dopo la posizione cancellati.

Non sono sicuro degli altri tipi di contenitori. In ogni caso, penso che sarebbe sicuro assumere che tutti gli iteratori diventino invalidi dopo una cancellazione. Se hai davvero bisogno di informazioni molto specifiche, puoi sempre verificarle. Raramente ho bisogno di questo perché grazie al mio stile di codifica piuttosto conservatore.

1

In generale, non importa se si memorizza nella cache l'iteratore finale. Se ritieni che sia importante, dovresti già utilizzare un profiler sul tuo codice e riuscire a profilare entrambe le varianti. Sospetto che potrebbe differire a seconda del tipo di contenitore, ma un profilatore sarebbe l'unico modo per sapere con certezza dato il tuo compilatore, ottimizzazioni e fornitore STL.

2

In generale è una buona idea per memorizzare nella cache un iteratore fine (in particolare contenitori STL) a fini di efficienza e di velocità?

Se si utilizzano gli algoritmi del contenitore STL, la memorizzazione nella cache dell'iter iter finale avviene comunque (mentre si passa il risultato di container.end() come parametro).

Se si modifica la memoria del contenitore (inserire/rimuovere elementi) è una cattiva idea.

Inoltre, il caching per l'efficienza raramente ha molto senso: nella maggior parte dei casi la fine() è delineata dal compilatore e, quando non lo è, è molto probabile che l'efficienza non si blocchi nel risultato finale() essere messo in cache YMMV però.

2

Le regole di invalidazione (per gli iteratori) sono definite in modo molto esplicito per ogni tipo di contenitore. Trovo il sito SGI molto utile http://www.sgi.com/tech/stl/table_of_contents.html

In particolare per i vettori trovo:

[5] iteratori di un vettore vengono invalidate quando la sua memoria viene riallocata. Inoltre, l'inserimento o l'eliminazione di un elemento nel mezzo di un vettore invalida tutti gli iteratori che puntano agli elementi che seguono il punto di inserimento o di cancellazione. Ne consegue che è possibile evitare iteratori di un vettore di essere invalidati se si utilizza di riserva() per preallocare la quantità di memoria il vettore potrà mai usare, e se tutte le inserzioni e delezioni sono alla fine del vettore.