2010-08-02 20 views
5

tengo los dos bucles siguientes:Pregunta sobre velocidad de lazo

#include <iostream> 
#include <stdlib.h> 
#include <time.h> 

using namespace std; 
int main(){ 

    int start=clock(); 
    for (int i=0;i<100;i++) 
     cout<<i<<" "<<endl; 
    cout<<clock()-start<<"\n"<<endl; 
    cout<<"\n"; 

    int start1=clock(); 
    for (int j=0;j<100;++j) 
     cout<<j<<" "<<endl; 
    cout<<"\n"; 
    cout<<clock()-start1<<" \n "<<endl; 

    return 0; 
} 

me encontré que tres veces. En las dos primeras carreras, el segundo ciclo fue el más rápido pero, en la tercera ejecución, el primer ciclo fue el más rápido. ¿Qué significa esto? ¿Cual es mejor? ¿Depende de la situación?

+4

100 iteraciones pueden ser demasiado pequeñas para sacar conclusiones ... – Vladimir

+0

Otra forma de comprobar cómo funciona su El código que está haciendo en el área de rendimiento es mirar el ensamblaje generado. Es probable que vea * lo mismo * generado para cada ciclo. El incremento pre-versus-post para tipos primitivos es el mismo. – GManNickG

+0

@GMan: ¿cómo puede ser lo mismo si la semántica es diferente? De acuerdo: el optimizador probablemente detectará que el valor de retorno del postfijo no se utilizará y, por lo tanto, generará el mismo desensamblaje. – xtofl

Respuesta

5

En su caso, es probable que el error de medición estándar y que no importa ¿Utiliza postincremento o preincremento . Para tipos estándar (int, byte ...) no importa.

PERO que debe acostumbrarse a utilizar preincremento porque hay consecuencias en el rendimiento si los está utilizando en una clase, dependiendo de cómo se implementan los operadores. operador de incremento posterior i ++ tiene que hacer una copia del objeto

Por ejemplo:

class integer 
{ 
public: 
    integer(int t_value) 
    : m_value(t_value) 
    { 
    } 

    int get_value() { return m_value; } 

    integer &operator++() // pre increment operator 
    { 
    ++m_value; 
    return *this; 
    } 

    integer operator++(int) // post increment operator 
    { 
    integer old = *this; // a copy is made, it's cheap, but it's still a copy 
    ++m_value; 
    return old; // Return old copy 
    } 

private: 
    int m_value; 

Tome un vistazo a la siguiente respuesta

StackOverflow: i++ less efficient than ++i, how to show this?

+1

No solo se ve afectada la eficiencia, en realidad _ significa algo diferente. – xtofl

+0

@xtofl: ¿Cómo es eso? (La discusión trata sobre el incremento pre/post como una expresión separada). – UncleBens

1

Significa que debe usar técnicas estadísticas para determinar qué bucle es más rápido (y espero que esas técnicas establezcan que estaban prácticamente a la par).

Tiene no idea de lo que su CPU está haciendo por encima de la carga que está poniendo aquí.

Podría ser iniciar tareas programadas, manejar interrupciones, todo tipo de cosas que podrían sesgar los resultados.

Es posible que tenga que hacer un millón de ejecuciones, descartar los valores atípicos y promediar el resto para obtener una muestra decente.

Por encima de todo, un centenar de iteraciones no es mucho, sobre todo desde que estás haciendo llamadas de función a la que puede cout así pantano el tiempo haciendo el control de bucle.

Cuando ejecuto controles en UNIX, I no usa tiempo transcurrido por este mismo motivo. El sistema y el tiempo del usuario indican cuántos segundos estuvo en uso la CPU para el proceso determinado, independientemente del tiempo transcurrido.

1

++ debería dar como resultado el mismo código de máquina que i ++ con resultado no utilizado en cualquier compilador half-decente (siempre que no esté sobrecargado de una manera elegante, que no puede ser el caso de int). E incluso si no es así (tendrías que desenterrar un compilador bastante estúpido del siglo pasado), la diferencia es tan pequeña que nunca deberías preocuparte por ello. Bueno, hay casos en los que necesitas exprimir hasta el mínimo rendimiento, pero los menos de nosotros entramos en esta situación. E incluso entonces, el cuerpo del bucle tiene mucho más potencial de optimización (incluso en su simplificado ejemplo: la E/S es mucho más costosa que copiar una palabra de máquina).

O en una sola frase: la optimización prematura es la raíz de todos los males.

4

Estos bucles son equivalentes para la variable de inducción de tipo int. Las preguntas de incremento gradual frente a incremento previo se han respondido aquí varias veces. Intenta buscar un poco en los archivos.

Además, la operación de incremento lleva solo una pequeña fracción de tiempo en comparación con el IO estándar. Su prueba mide la velocidad del IO en lugar de la velocidad de las operaciones de incremento.

10

El tiempo de ejecución de sus bucles está predominantemente dominado por las operaciones de entrada y salida. Esto significa que el tiempo que observa 1) no tiene nada que ver con el rendimiento real del ciclo (es decir, i++ frente a ++j), 2) es bastante impredecible e inestable (esencialmente aleatorio).

En otras palabras, su experimento no tiene sentido. Significa absolutamente nada.

Finalmente, en situaciones en las que no se utiliza el resultado del operador ++ incorporado, no hay absolutamente ninguna diferencia entre el incremento de prefijo y el prefijo. En cualquier compilador razonable, ambos bucles tendrán un rendimiento absolutamente idéntico.

+2

Apuesto a que si compila esto en lenguaje ensamblador que los dos bucles serán exactamente los mismos – Jaka

-1

++ i es preincremento
i ++ es postincremento

ver Operator para más detalles

+0

creo que él lo sabe. – GManNickG

+0

Respuesta simple. Astuto. Si hubiera agregado algo sobre el significado del incremento previo al post, le hubiese dado +1d. – xtofl

1

Por lo que veo, el único la diferencia en los bucles es el incremento pre/post de la variable de bucle. La mayor parte del tiempo de proceso se gastará en el cout. Incrementar será de tiempo insignificante en comparación.

3

Viejo 3.4.4 GCC hace esto:

primer bucle:

.L11: 
     cmpl $99, -8(%ebp) 
     jg  .L12 
     subl $8, %esp 
     pushl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ 
     subl $12, %esp 
     pushl $.LC0 
     subl $12, %esp 
     pushl -8(%ebp) 
     pushl $_ZSt4cout 
.LCFI7: 
     call _ZNSolsEi 
     addl $20, %esp 
     pushl %eax 
     call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc 
     addl $20, %esp 
     pushl %eax 
.LCFI8: 
     call _ZNSolsEPFRSoS_E 
     addl $16, %esp 
     leal -8(%ebp), %eax 
     incl (%eax) 
     jmp  .L11 
.L12: 

segundo bucle:

.L14: 
     cmpl $99, -12(%ebp) 
     jg  .L15 
     subl $8, %esp 
     pushl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ 
     subl $12, %esp 
     pushl $.LC0 
     subl $12, %esp 
     pushl -12(%ebp) 
     pushl $_ZSt4cout 
.LCFI13: 
     call _ZNSolsEi 
     addl $20, %esp 
     pushl %eax 
     call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc 
     addl $20, %esp 
     pushl %eax 
.LCFI14: 
     call _ZNSolsEPFRSoS_E 
     addl $16, %esp 
     leal -12(%ebp), %eax 
     incl (%eax) 
     jmp  .L14 
.L15: 

¿Puedes encontrar las diferencias? :) (además de que i y j están en diferentes ubicaciones en la pila -8 (% ebp) y -12 (% ebp))