2009-04-21 20 views
42

¿Cuál es mejor (o más rápido), un C++ for loop o el operador foreach provisto por Qt? Por ejemplo, la siguiente condición'for' loop vs Qt's 'foreach' en C++

QList<QString> listofstrings; 

¿Qué es mejor?

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

Sólo una pequeña nota que si usted no planea en la modificación de la variable de bucle foreach, se debe utilizar un QString y en su lugar - que tiene implicaciones en velocidad también. – swongu

+0

Aquí hay una explicación de por qué quiere tener cuidado para incluir la const: http://labs.qt.nokia.com/2009/01/23/iterating-efficiently/ –

+1

La adición importante a esta pregunta es que usted debe nunca use Qt 'foreach' para contenedores que no sean Qt, lo más probable es que se realice una copia profunda y esto es lo que no quiere, incluso sin crear un perfil de antemano. – Predelnik

Respuesta

143

Realmente no importa en la mayoría de los casos.

La gran cantidad de preguntas sobre StackOverflow sobre si este método o ese es más rápido, desmienten el hecho de que, en la gran mayoría de los casos, el código pasa la mayor parte del tiempo esperando que los usuarios hagan algo.

Si son realmente preocupados, perfílgalo usted mismo y actúe según lo que encuentre.

Pero creo que lo más probable es que descubra que solo en el trabajo más intenso de procesamiento de datos hace esta pregunta. La diferencia puede ser solo de unos segundos e incluso entonces, solo cuando se procesan grandes cantidades de elementos.

Obtenga su código de trabajo primero. Luego haz que funcione rápido (y solo si encuentras un problema de rendimiento real).

El tiempo dedicado a la optimización antes de que haya terminado la funcionalidad y pueda realizar un perfil adecuado, es en su mayoría tiempo perdido.

+48

El número pasó la etapa de broma hace mucho tiempo. Deberíamos escribir un bot de respuesta que busque "cuál es más rápido" y respuestas automáticas: perfilarlo – JaredPar

+2

Ver http: // stackoverflow.com/questions/771092/is-method-a-faster-method-b: veamos si sobrevive :-) – paxdiablo

+20

No es que realmente se aplique tanto al simple ejemplo, pero lo que me gusta decir es " El código incorrecto es tan desoptimizado como puedes obtener ". –

2

El foreach de Qt tiene una sintaxis más clara para el bucle for IMHO, por lo que es mejor en ese sentido. En cuanto al rendimiento, dudo que haya algo en él.

En su lugar, podría considerar usar el BOOST_FOREACH, ya que es un bucle de fantasía bien pensado, y es portátil (y presumiblemente llegará a C++ algún día y también será una prueba de futuro).

1

Para pequeñas colecciones, debería importar y el foreach tiende a ser más claro.

Sin embargo, para colecciones más grandes, para empezará a vencer a foreach en algún momento. (suponiendo que el operador 'at()' es eficiente.

Si esto es realmente importante (y asumo que es así porque lo está preguntando) entonces lo mejor que puede hacer es medirlo. Un perfilador debe hacer truco, o podría construir una versión de prueba con algunos instrumentos.

0

Esperaría que foreach funcione nominalmente más rápido en algunos casos, y el mismo en otros, excepto en los casos en que los artículos son una matriz real en cuyo caso el la diferencia de rendimiento es insignificante

Si se implementa sobre un enumerador, puede ser más eficiente que una indexación directa, dependiendo de la implementación ción. Es poco probable que sea menos eficiente. Por ejemplo, si alguien expuso un árbol equilibrado como indexable y enumerable, entonces foreach será decentemente más rápido. Esto se debe a que cada índice tendrá que buscar de forma independiente el elemento al que se hace referencia, mientras que un enumerador tiene el contexto del nodo actual para navegar de manera más eficiente al siguiente ont.

Si tiene una matriz real, depende de la implementación del lenguaje y la clase si foreach será más rápido para el mismo que para.

Si la indexación es un desplazamiento literal de la memoria (como C++), entonces debe ser ligeramente más rápido ya que está evitando una llamada a función. Si la indexación es un direccionamiento indirecto como una llamada, entonces debería ser el mismo.

Dicho todo esto ... Me resulta difícil encontrar un caso para la generalización aquí. Este es el último tipo de optimización que debería estar buscando, incluso si hay un problema de rendimiento en su aplicación. Si tiene un problema de rendimiento que puede resolverse cambiando la forma en que itera, realmente no tiene un problema de rendimiento. Tienes un ERROR porque alguien escribió un iterador realmente malo o un indexador realmente malo.

0

Puede consultar la función for_each del STL. No sé si será más rápido que las dos opciones que presenta, pero está más estandarizado que el foreach de Qt y evita algunos de los problemas con los que puede toparse con un bucle for normal (es decir, indexación fuera de límites y dificultades) con traducir el bucle a una estructura de datos diferente).

+2

IMO, STL no es más "estándar" que Qt. El código STL es a menudo extremadamente feo. Si tiene que integrarse con aplicaciones/librerías que no sean Qt C++, entonces puede valer la pena. Si solo estás construyendo una aplicación Qt, usar las funciones STL no te ayuda para nada, en cambio, estarás encasillando todo el tiempo. –

19

En primer lugar, me gustaría decir que estoy de acuerdo con Pax, y que la velocidad probablemente no entra en ella. foreach gana sin problemas en función de la legibilidad, y eso es suficiente en el 98% de los casos.

Pero, por supuesto, los chicos Qt han mirado en él y, de hecho hecho algunos perfiles: http://blog.qt.io/blog/2009/01/23/iterating-efficiently/

La principal lección que le quita a saber: utilizar referencias const en sólo lectura bucles, ya que evita la creación de temporal instancias. También hace que el propósito del ciclo sea más explícito, independientemente del método de bucle que utilice.

3

Primero, estoy completamente de acuerdo con la respuesta de que "no importa". Elija la solución más limpia y optimice si se convierte en un problema.

Pero otra manera de verlo es que a menudo, la solución más rápida es la que describe su intención con mayor precisión. En este caso, foreach de QT dice que le gustaría aplicar alguna acción para cada elemento en el contenedor.

Un ciclo simple para indicar que desea un contador i. Desea agregar repetidamente uno a este valor i, y siempre que sea menor que el número de elementos en el contenedor, le gustaría realizar alguna acción.

En otras palabras, el plain for loop sobreespecifica el problema. Agrega muchos requisitos que no son parte de lo que estás tratando de hacer. Usted no cuida sobre el contador de bucles. Pero tan pronto como escriba un ciclo for, tiene que estar allí.

Por otro lado, las personas de QT no han hecho promesas adicionales que puedan afectar el rendimiento. Simplemente garantizan iterar a través del contenedor y aplicar una acción a cada uno.

En otras palabras, a menudo la solución más limpia y elegante también es la más rápida.

14

Realmente no importa. Las probabilidades son que si su programa es lento, este no es el problema. Sin embargo, se debe tener en cuenta que no se hace una comparación completamente igual.Qt de foreach es más similar a este (este ejemplo utilizará QList<QString>):

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

La macro es capaz de hacer esto mediante el uso de algunas extensiones del compilador (como GCC de __typeof__) para obtener el tipo de contenedor pasado. También imagina que el impulso BOOST_FOREACH es muy similar en concepto.

La razón por la que su ejemplo no es justo es porque su versión que no es Qt está agregando trabajo adicional.

Está indexando en lugar de realmente iterar. Si está utilizando un tipo con asignación no contigua (sospecho que este podría ser el caso con QList<>), la indexación será más cara ya que el código tiene que calcular "dónde" está el n-ésimo elemento.

Dicho eso. Es todavía no importa. La diferencia de tiempo entre esas dos piezas de código será insignificante si existe en absoluto. No pierdas tu tiempo preocupándote por eso. Escriba lo que encuentre más claro y comprensible.

EDIT: Como beneficio adicional, actualmente estoy firmemente a favor de la versión C++ 11 de iteración recipiente, es limpia, concisa y simple:

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

Su afirmación sobre el rendimiento 'at()' vs 'operator []' no es correcta para Qt. Ambos realizan o no la verificación de límites dependiendo de las opciones de compilación. Las excepciones no se usan en Qt. En contraste, la [documentación dice] (http://doc.qt.io/qt-5/qlist.html) "' at() 'puede ser más rápido que' operator []() ', porque nunca causa un copia profunda para que ocurra ". –

+0

@TimHoffmann, Buen punto, voy a hacer una edición :-) –

9

No quiero responder a la pregunta que es más rápido, pero quiero decir cuál es mejor.

El mayor problema con el foreach de Qt es el hecho de que toma una copia de su contenedor antes de iterar sobre él. Podrías decir 'esto no importa porque las clases Qt se vuelven a contar', pero debido a que se usa una copia, en realidad no cambias tu contenedor original.

En resumen, el foreach de Qt solo se puede utilizar para bucles de solo lectura y, por lo tanto, debe evitarse. Qt estará encantado de permitirle escribir un ciclo foreach que cree que actualizará/modificará su contenedor, pero al final todos los cambios se descartarán.

+0

Estoy de acuerdo. Me encontré con un 'foreach' durante el perfilado: Resultó que había cambiado un' QList' por un 'QVarLengthArray' (nuevamente después del perfilado) y encontré el' foreach' copiando 'QVarLengthArray'. Oops.Fui en busca de un bucle 'for' normal y ese seguimiento de la pila ya no aparece en el generador de perfiles. – Ben