significado postincremento numero entre diferencia c performance optimization post-increment pre-increment

postincremento - i++ significado



¿Hay una diferencia de rendimiento entre i++ y++ i en C? (13)

¿Existe una diferencia de rendimiento entre i++ y ++i si el valor resultante no se utiliza?


@Mark Aunque el compilador puede optimizar la copia temporal (basada en la pila) de la variable y gcc (en versiones recientes) lo está haciendo, no significa que todos los compiladores siempre lo harán.

Acabo de probarlo con los compiladores que utilizamos en nuestro proyecto actual y 3 de 4 no lo optimizan.

Nunca asuma que el compilador lo hace bien, especialmente si el código posiblemente más rápido, pero nunca más lento, es tan fácil de leer.

Si no tiene una implementación realmente estúpida de uno de los operadores en su código:

Siempre preferí ++ i sobre i ++.


Aquí hay una observación adicional si te preocupa la micro optimización. La reducción de los bucles puede ''posiblemente'' ser más eficiente que el incremento de bucles (dependiendo de la arquitectura del conjunto de instrucciones, por ejemplo, ARM), dado:

for (i = 0; i < 100; i++)

En cada bucle tendrás una instrucción para cada uno:

  1. Sumando 1 a i .
  2. Compara si i es menor que 100 .
  3. Una rama condicional si i es menor que 100 .

Considerando que un bucle decremento:

for (i = 100; i != 0; i--)

El bucle tendrá una instrucción para cada uno de:

  1. Decremento i , configurando el indicador de estado de registro de la CPU.
  2. Una rama condicional que depende del estado del registro de la CPU ( Z==0 ).

¡Por supuesto que esto funciona solo cuando decrementa a cero!

Recordado de la Guía del desarrollador del sistema ARM.


De la eficiencia versus la intención de Andrew Koenig:

Primero, está lejos de ser obvio que ++i es más eficiente que i++ , al menos en lo que concierne a las variables enteras.

Y

Entonces, la pregunta que uno debería hacerse no es cuál de estas dos operaciones es más rápida, sino cuál de estas dos operaciones expresa con mayor precisión lo que usted está tratando de lograr. Acepto que si no está usando el valor de la expresión, nunca hay una razón para usar i++ lugar de ++i , porque nunca hay una razón para copiar el valor de una variable, incrementar la variable y luego lanzar la copia lejos

Entonces, si el valor resultante no se usa, yo usaría ++i . Pero no porque sea más eficiente: porque declara correctamente mi intención.


En C, el compilador generalmente puede optimizarlos para que sean iguales si el resultado no se utiliza.

Sin embargo, en C ++, si se utilizan otros tipos que proporcionan sus propios operadores ++, es probable que la versión de prefijo sea más rápida que la versión de postfix. Entonces, si no necesita la semántica de postfix, es mejor usar el operador de prefijo.


En primer lugar: la diferencia entre i++ y ++i es despreciable en C.

A los detalles.

1. El conocido problema de C ++: ++i es más rápido

En C ++, ++i es más eficiente si i es algún tipo de objeto con un operador de incremento sobrecargado.

¿Por qué?
En ++i , el objeto se incrementa primero, y posteriormente se puede pasar como una referencia constante a cualquier otra función. Esto no es posible si la expresión es foo(i++) porque ahora el incremento debe realizarse antes de llamar a foo() , pero el valor antiguo debe pasarse a foo() . En consecuencia, el compilador se ve obligado a hacer una copia de i antes de ejecutar el operador de incremento en el original. Las llamadas adicionales de constructor / destructor son la parte mala.

Como se señaló anteriormente, esto no se aplica a los tipos fundamentales.

2. El hecho poco conocido: i++ puede ser más rápido

Si no es necesario llamar a un constructor / destructor, lo cual es siempre el caso en C, ++i e i++ deberían ser igualmente rápidos, ¿verdad? No. Son virtualmente igual de rápidos, pero puede haber pequeñas diferencias, por lo que la mayoría de los demás respondedores se equivocaron.

¿Cómo puede i++ ser más rápido?
El punto es dependencias de datos. Si el valor debe cargarse desde la memoria, dos operaciones posteriores deben realizarse con él, incrementándolo y usándolo. Con ++i , la incrementación debe realizarse antes de poder utilizar el valor. Con i++ , el uso no depende del incremento, y la CPU puede realizar la operación de uso en paralelo a la operación de incremento. La diferencia es a lo sumo un ciclo de CPU, por lo que es realmente despreciable, pero está ahí. Y es al revés de lo que muchos esperarían.


Mi C está un poco oxidada, así que me disculpo por adelantado. Speedwise, puedo entender los resultados. Pero, estoy confundido en cuanto a cómo ambos archivos salieron al mismo hash MD5. Tal vez un bucle for se ejecute igual, pero ¿las siguientes 2 líneas de código no generan un ensamblaje diferente?

myArray[i++] = "hello";

vs

myArray[++i] = "hello";

El primero escribe el valor en la matriz y luego incrementa i. Los segundos incrementos que luego escribo en la matriz. No soy experto en ensamblajes, pero no veo cómo estas dos líneas de código diferentes generarían el mismo ejecutable.

Sólo mis dos centavos.


Por favor, no permita que la pregunta de "cuál de ellos es más rápido" sea el factor decisivo para usar. Es probable que nunca le importe tanto, y además, el tiempo de lectura del programador es mucho más costoso que el tiempo de la máquina.

Usa lo que tenga más sentido para el humano que lee el código.


Resumen ejecutivo: No.

i++ podría ser potencialmente más lento que ++i , ya que el antiguo valor de i podría necesitar guardarse para su uso posterior, pero en la práctica todos los compiladores modernos optimizarán esto.

Podemos demostrar esto mirando el código de esta función, tanto con ++i como i++ .

$ cat i++.c extern void g(int i); void f() { int i; for (i = 0; i < 100; i++) g(i); }

Los archivos son los mismos, excepto para ++i y i++ :

$ diff i++.c ++i.c 6c6 < for (i = 0; i < 100; i++) --- > for (i = 0; i < 100; ++i)

Los compilaremos y también obtendremos el ensamblador generado:

$ gcc -c i++.c ++i.c $ gcc -S i++.c ++i.c

Y podemos ver que tanto el objeto generado como los archivos de ensamblador son los mismos.

$ md5 i++.s ++i.s MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e $ md5 *.o MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22 MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22


Se me ocurre una situación en la que Postfix es más lento que el incremento de prefijo:

Imagine que un procesador con registro A se usa como acumulador y es el único registro que se usa en muchas instrucciones (algunos microcontroladores pequeños son así).

Ahora imagine el siguiente programa y su traducción en un montaje hipotético:

Incremento de prefijo:

a = ++b + c; ; increment b LD A, [&b] INC A ST A, [&b] ; add with c ADD A, [&c] ; store in a ST A, [&a]

Incremento de Postfix:

a = b++ + c; ; load b LD A, [&b] ; add with c ADD A, [&c] ; store in a ST A, [&a] ; increment b LD A, [&b] INC A ST A, [&b]

Observe cómo el valor de b fue forzado a ser recargado. Con el incremento de prefijo, el compilador solo puede incrementar el valor y seguir adelante con su uso, posiblemente evitando volver a cargarlo ya que el valor deseado ya está en el registro después del incremento. Sin embargo, con el incremento postfix, el compilador tiene que lidiar con dos valores, uno antiguo y otro incrementado que, como se muestra arriba, da como resultado un acceso más a la memoria.

Por supuesto, si el valor del incremento no se usa, como un solo i++; declaración, el compilador puede (y lo hace) simplemente generar una instrucción de incremento independientemente del uso de prefijo o postfijo.

Como nota al margen, me gustaría mencionar que una expresión en la que hay un b++ no se puede convertir simplemente en uno con ++b sin ningún esfuerzo adicional (por ejemplo, agregando un - 1 ). Entonces, comparar los dos si son parte de alguna expresión no es realmente válido. A menudo, cuando usas b++ dentro de una expresión no puedes usar ++b , por lo que incluso si ++b fuera potencialmente más eficiente, simplemente sería incorrecto. La excepción es, por supuesto, si la expresión lo pide (por ejemplo, a = b++ + 1; que se puede cambiar a a = ++b; ).


Siempre prefiero el pre-incremento, sin embargo ...

Quería señalar que incluso en el caso de llamar a la función de operador ++, el compilador podrá optimizar el temporal si la función está en línea. Como el operador ++ suele ser corto y, a menudo, implementado en el encabezado, es probable que quede en línea.

Por lo tanto, para propósitos prácticos, probablemente no haya mucha diferencia entre el rendimiento de las dos formas. Sin embargo, siempre prefiero el pre-incremento, ya que parece mejor expresar directamente lo que estoy tratando de decir, en lugar de confiar en el optimizador para averiguarlo.

Además, dar menos posibilidades al optmizer significa que el compilador se ejecuta más rápido.


Tomando una hoja de Scott Meyers, Más efectivo c ++ Artículo 6: Distinguir entre las formas de prefijo y postfijo de las operaciones de incremento y decremento .

La versión del prefijo siempre se prefiere sobre el postfix en lo que respecta a los objetos, especialmente en lo que respecta a los iteradores.

La razón de esto si nos fijamos en el patrón de llamada de los operadores.

// Prefix Integer& Integer::operator++() { *this += 1; return *this; } // Postfix const Integer Integer::operator++(int) { Integer oldValue = *this; ++(*this); return oldValue; }

Mirando este ejemplo, es fácil ver cómo el operador de prefijo siempre será más eficiente que el postfix. Debido a la necesidad de un objeto temporal en el uso del postfix.

Es por eso que cuando ves ejemplos usando iteradores, siempre usan la versión de prefijo.

Pero como señala para int''s, efectivamente no hay diferencia debido a la optimización del compilador que puede tener lugar.


Una mejor respuesta es que ++i a veces seré más rápido pero nunca más lento.

Todo el mundo parece estar asumiendo que i es un tipo incorporado normal como int . En este caso no habrá diferencia medible.

Sin embargo, si i es un tipo complejo, entonces es posible que encuentre una diferencia mensurable. Para i++ debe hacer una copia de su clase antes de incrementarla. Dependiendo de lo que esté involucrado en una copia, podría ser más lento ya que con ++it puede devolver el valor final.

Foo Foo::operator++() { Foo oldFoo = *this; // copy existing value - could be slow // yadda yadda, do increment return oldFoo; }

Otra diferencia es que con ++i tiene la opción de devolver una referencia en lugar de un valor. Nuevamente, dependiendo de lo que esté involucrado en hacer una copia de su objeto, esto podría ser más lento.

Un ejemplo del mundo real de dónde puede ocurrir esto sería el uso de iteradores. Es poco probable que copiar un iterador sea un cuello de botella en su aplicación, pero aún así es una buena práctica adquirir el hábito de usar ++i lugar de i++ donde el resultado no se ve afectado.


Respuesta corta:

Nunca hay ninguna diferencia entre i++ y ++i en términos de velocidad. Un buen compilador no debe generar código diferente en los dos casos.

Respuesta larga:

Lo que cualquier otra respuesta no menciona es que la diferencia entre ++i versus i++ solo tiene sentido dentro de la expresión que se encuentra.

En el caso de for(i=0; i<n; i++) , el i++ está solo en su propia expresión: hay un punto de secuencia antes del i++ y otro después. Por lo tanto, el único código de máquina generado es "aumentar i en 1 " y está bien definido cómo se secuencia esto en relación con el resto del programa. Entonces, si lo cambiase por el prefijo ++ , no importaría en lo más mínimo, solo obtendría el código de la máquina "aumentar i en 1 ".

Las diferencias entre ++i y i++ solo importan en expresiones como array[i++] = x; versus array[++i] = x; . Algunos pueden argumentar y decir que el postfix será más lento en tales operaciones debido a que el registro en el que resido debo volver a cargarse más tarde. Pero tenga en cuenta que el compilador es libre de ordenar sus instrucciones de la manera que le plazca, siempre y cuando no "rompa el comportamiento de la máquina abstracta" como lo llama el estándar C.

Entonces, mientras puedes asumir que la array[i++] = x; se traduce al código de máquina como:

  • Almacene el valor de i en el registro A.
  • Almacenar la dirección de la matriz en el registro B.
  • Agrega A y B, almacena los resultados en A.
  • En esta nueva dirección representada por A, almacene el valor de x.
  • Almacene el valor de i en el registro A // ineficiente porque aquí hay instrucciones adicionales, ya lo hicimos una vez.
  • Incremento de registro A.
  • Tienda registro A en i .

el compilador también podría producir el código de manera más eficiente, como por ejemplo:

  • Almacene el valor de i en el registro A.
  • Almacenar la dirección de la matriz en el registro B.
  • Agrega A y B, almacena los resultados en B.
  • Incremento de registro A.
  • Tienda registro A en i .
  • ... // resto del código.

Solo porque usted, como programador de C, está entrenado para pensar que el postfix ++ ocurre al final, el código de la máquina no tiene que ser ordenado de esa manera.

Por lo tanto, no hay diferencia entre el prefijo y el postfijo ++ en C. Ahora, como debe ser su programador en C, las personas que usan el prefijo de forma inconsistente en algunos casos y el postfijo en otros casos, sin ningún motivo. Esto sugiere que no están seguros de cómo funciona C o que tienen un conocimiento incorrecto del idioma. Esto siempre es una mala señal, a su vez sugiere que están tomando otras decisiones cuestionables en su programa, basadas en supersticiones o "dogmas religiosos".

"Prefijo ++ siempre es más rápido" es, de hecho, uno de esos falsos dogmas que es común entre los programadores de C en potencia.