2009-04-21 16 views
42

Quale è migliore (o più veloce), un ciclo C++ for o l'operatore foreach fornito da Qt? Ad esempio, la seguente condizione'per' loop vs Qt's 'foreach' in C++

QList<QString> listofstrings; 

Quale è meglio?

foreach(QString str, listofstrings) 
{ 
    //code 
} 

o

int count = listofstrings.count(); 
QString str = QString(); 
for(int i=0;i<count;i++) 
{ 
    str = listofstrings.at(i); 
    //Code 
} 
+4

Solo una breve nota che se non si ha intenzione di modificare l'indice del ciclo foreach, è necessario utilizzare un const QString & invece - che ha implicazioni su anche la velocità. – swongu

+0

Ecco una spiegazione del perché si vuole fare attenzione a includere il const: http://labs.qt.nokia.com/2009/01/23/iterating-efficiently/ –

+1

L'importante aggiunta a questa domanda è che si dovrebbe mai e poi mai usare Qt 'foreach' per contenitori non Qt, molto probabilmente verrà eseguita una copia profonda e questa è la cosa che non si vuole anche senza averlo prima profilato. – Predelnik

risposta

143

E 'davvero non importa, nella maggior parte dei casi.

Il gran numero di domande su StackOverflow riguardanti se questo metodo o quel metodo è più veloce, smentiscono il fatto che, nella stragrande maggioranza dei casi, il codice trascorre la maggior parte del tempo in attesa che gli utenti facciano qualcosa.

Se il numero è il davvero preoccupato, descrivilo da solo e agisci su ciò che trovi.

Ma penso che sarà più probabile trovare che solo nel lavoro di elaborazione dati pesante più intensa fa questa materia domanda. La differenza potrebbe essere solo di un paio di secondi e anche allora, solo quando si elaborano enormi numeri di elementi.

ottenere il codice di lavoro prima. Quindi fallo funzionare fast (e solo se trovi un problema di prestazioni).

Il tempo trascorso ottimizzazione prima di aver finito la funzionalità e può correttamente profilo, è per lo più sprecato tempo.

+48

Il numero ha superato la fase di scherzo molto tempo fa. Dovremmo scrivere un bot di risposta che cerchi "più veloce" e risposte automatiche: profile it – JaredPar

+2

Vedi http: // StackOverflow.it/questions/771092/is-method-a-faster-than-method-b: vediamo se sopravvive :-) – paxdiablo

+20

Non che si applichi così tanto all'esempio semplice, ma quello che mi piace dire è, " Il codice errato è non ottimizzato come puoi ottenere ". –

2

Il foreach da Qt ha una sintassi più chiara per il ciclo for IMHO, quindi è meglio in questo senso. Per quanto riguarda le prestazioni, dubito che ci sia qualcosa in esso.

Si potrebbe considerare l'uso del BOOST_FOREACH, invece, in quanto è una fantasia ben pensato per il ciclo, ed è portatile (e presumibilmente farà la sua strada in C++, un giorno ed è a prova di futuro troppo).

1

Per piccole collezioni, occorre materia e foreach tende ad essere più chiaro.

Tuttavia, per le collezioni più grandi, per cominceranno a battere foreach ad un certo punto. (supponendo che l'operatore 'at()' sia efficiente

Se questo è veramente importante (e presumo che lo sia da quando lo si sta chiedendo), allora la cosa migliore da fare è misurarlo. trucco, oppure potresti costruire una versione di prova con alcuni strumenti

0

Mi aspetterei che foreach funzioni nominalmente più veloce in alcuni casi e lo stesso in altri, tranne nei casi in cui gli elementi sono un array effettivo nel qual caso il la differenza di performance è trascurabile

Se è implementato su un enumeratore, è essere più efficiente di un indicizzazione lineare, a seconda dell'implementazione zione. È improbabile che sia meno efficiente. Ad esempio, se qualcuno ha esposto un albero bilanciato sia come indicizzabile che enumerabile, allora foreach sarà decentemente più veloce. Questo perché ogni indice dovrà trovare indipendentemente l'elemento di riferimento, mentre un enumeratore ha il contesto del nodo corrente per navigare in modo più efficiente verso il prossimo ont.

Se si dispone di un array effettivo, dipende dall'implementazione della lingua e della classe se foreach sarà più veloce per lo stesso di.

Se l'indicizzazione è un offset di memoria letterale (come C++), quindi per dovrebbe essere leggermente più veloce poiché si evita una chiamata di funzione. Se l'indicizzazione è un indiretto, proprio come una chiamata, allora dovrebbe essere lo stesso.

Tutto ciò detto ... Trovo difficile trovare un caso per la generalizzazione qui. Questo è l'ultimo tipo di ottimizzazione che dovresti cercare, anche se c'è un problema di prestazioni nella tua applicazione. Se si ha un problema di prestazioni che può essere risolto cambiando il modo in cui si itera, non si ha realmente un problema di prestazioni. Hai un bug, perché qualcuno ha scritto un iteratore davvero schifoso, o un indicizzatore davvero schifoso.

0

Si potrebbe guardare la funzione for_each dell'STL. Non so se sarà più veloce delle due opzioni che presenti, ma è più standardizzata della Qt foreach ed evita alcuni dei problemi che potresti incontrare con un ciclo for normale (ovvero l'indicizzazione e le difficoltà fuori limite con la traduzione del loop in una diversa struttura dati).

+2

IMO, STL non è più "standard" di Qt. Il codice in stile STL è spesso estremamente brutto. Se devi integrarti con app/lib non di Qt C++, potrebbe essere utile. Se stai solo costruendo un'app Qt, l'uso delle funzioni STL non ti aiuta affatto, ma continuerai a digitare in continuazione. –

19

Prima di tutto, vorrei solo dire che sono d'accordo con Pax e che probabilmente la velocità non vi entra. foreach vince le mani in base alla leggibilità, e questo è sufficiente nel 98% dei casi.

Ma, naturalmente, i ragazzi Qt hanno guardato dentro e realmente fatto qualche profiling: http://blog.qt.io/blog/2009/01/23/iterating-efficiently/

La lezione principale da togliere da questo è: utilizzare i riferimenti const a leggere loop solo in quanto evita la creazione di temporanea le istanze. Rende anche più esplicito lo scopo del ciclo, indipendentemente dal metodo di loop che usi.

3

In primo luogo, sono completamente d'accordo con la risposta che "non importa". Scegli la soluzione più pulita e ottimizza se diventa un problema.

Ma un altro modo di guardarlo è che spesso la soluzione più rapida è quella che descrive il tuo intento in modo più accurato. In questo caso, foreach di QT dice che desideri applicare un'azione per ciascun elemento nel contenitore.

Un ciclo normale indica che desideri un contatore i. Si desidera aggiungere ripetutamente uno a questo valore i, e fintanto che è inferiore al numero di elementi nel contenitore, si desidera eseguire qualche azione.

In altre parole, il ciclo semplice per sovraspecifica il problema. Aggiunge molti requisiti che non fanno realmente parte di ciò che stai cercando di fare. Non si cura per il contatore del ciclo. Ma non appena scrivi un ciclo for, deve esserci.

D'altra parte, le persone del QT non hanno fatto ulteriori promesse che potrebbero influire sulle prestazioni. Garantiscono semplicemente di scorrere il contenitore e applicare un'azione a ciascuno.

In altre parole, spesso la soluzione più pulita ed elegante è anche la più veloce.

14

Non importa davvero. Le probabilità sono se il tuo programma è lento, questo non è il problema. Tuttavia, si dovrebbe notare che non si effettua un confronto completamente uguale.Qt di foreach è più simile a questo (questo esempio utilizzerà QList<QString>):

for(QList<QString>::iterator it = Con.begin(); it != Con.end(); ++it) { 
    QString &str = *it; 
    // your code here 
} 

La macro è in grado di farlo utilizzando alcune estensioni del compilatore (come GCC di __typeof__) per ottenere il tipo di contenitore passato. Immaginate anche che il boost BOOST_FOREACH sia molto simile nel concetto.

Il motivo per cui il tuo esempio non è giusto è che la tua versione non-Qt sta aggiungendo lavoro extra.

Si sta indicizzando invece di iterare davvero. Se si utilizza un tipo con allocazione non contigua (suppongo che questo potrebbe essere il caso con QList<>), l'indicizzazione sarà più costosa in quanto il codice deve calcolare "dove" è l'articolo n-esimo.

Detto questo. È ancora non importa. La differenza di temporizzazione tra questi due pezzi di codice sarà trascurabile se inesistente. Non sprecare il tuo tempo a preoccupartene. Scrivi quello che trovi più chiaro e comprensibile.

EDIT: Come bonus, attualmente sono a favore con forza la versione C++ 11 del contenitore di iterazione, è pulito, conciso e semplice:

for(QString &s : Con) { 
    // you code here 
} 
+0

La tua affermazione relativa alle prestazioni di 'at()' vs 'operator []' non è corretta per Qt. Entrambi eseguono o non eseguono il controllo dei limiti a seconda delle opzioni di compilazione. Le eccezioni non sono utilizzate in Qt. Al contrario, [la documentazione dice] (http://doc.qt.io/qt-5/qlist.html) "' a() 'può essere più veloce di' operator []() ', perché non causa mai un copia profonda per verificarsi. " –

+0

@TimHoffmann, buon punto, farò una modifica :-) –

9

Non voglio rispondere alla domanda che è più veloce, ma voglio dire quale è meglio.

Il problema più grande con il foreach di Qt è il fatto che richiede una copia del contenitore prima di iterarlo su di esso. Si potrebbe dire "questo non ha importanza perché le classi Qt vengono reimpostate" ma poiché si utilizza una copia in realtà non si cambia affatto il contenitore originale.

In breve, QT's foreach può essere utilizzato solo per cicli di sola lettura e pertanto deve essere evitato. Qt ti lascerà felicemente scrivere un ciclo foreach che pensi possa aggiornare/modificare il tuo contenitore ma alla fine tutte le modifiche verranno eliminate.

+0

Sono d'accordo. Mi sono imbattuto in un 'foreach' durante la profilazione: ho scoperto che avevo cambiato un' QList' in un 'QVarLengthArray' (di nuovo dopo la profilatura) e ho trovato il file' foreach' che copiava il mio 'QVarLengthArray'. Ops.Sono andato per un normale ciclo 'for' e quella traccia di stack non è più comparsa nel profiler. – Ben