¿Existe una diferencia de rendimiento entre i++
y ++i
si no se usa el valor resultante?
c
performance
optimization
post-increment
pre-increment
Mark Harrison
fuente
fuente
Respuestas:
Resumen ejecutivo: no.
i++
potencialmente podría ser más lento que++i
, ya que el valor anterior dei
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
yi++
.Los archivos son iguales, excepto por
++i
yi++
:Los compilaremos y también obtendremos el ensamblador generado:
Y podemos ver que tanto el objeto generado como los archivos ensambladores son iguales.
fuente
++i
lugar de hacerloi++
. No hay absolutamente ninguna razón para no hacerlo, y si su software pasa a través de una cadena de herramientas que no lo optimiza, su software será más eficiente. Teniendo en cuenta que es tan fácil de escribir++i
como de escribiri++
, realmente no hay excusa para no estar usando++i
en primer lugar.De Eficiencia versus intención de Andrew Koenig:
Y:
Entonces, si no se usa el valor resultante, lo usaría
++i
. Pero no porque sea más eficiente: porque indica correctamente mi intención.fuente
i++
la misma manera que codificoi += n
oi = i + n
, es decir, en la forma objeto del verbo objetivo , con el operando objetivo a la izquierda del operador verbal . En el caso dei++
, no hay un objeto correcto , pero la regla aún se aplica, manteniendo el objetivo a la izquierda del operador verbal .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 comoint
. En este caso no habrá diferencia medible.Sin embargo, si
i
es de tipo complejo, puede encontrar una diferencia medible. Para elloi++
debe hacer una copia de su clase antes de incrementarla. Dependiendo de lo que esté involucrado en una copia, de hecho podría ser más lento ya que con++it
usted solo puede devolver el valor final.Otra diferencia es que con
++i
usted 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 sigue siendo una buena práctica adquirir el hábito de usar en
++i
lugar dei++
donde el resultado no se ve afectado.fuente
Respuesta corta:
Nunca hay diferencia entre
i++
y++i
en términos de velocidad. Un buen compilador no debería generar un código diferente en los dos casos.Respuesta larga:
Lo que cualquier otra respuesta no menciona es que la diferencia entre
++i
versusi++
solo tiene sentido dentro de la expresión que se encuentra.En el caso de
for(i=0; i<n; i++)
, eli++
está solo en su propia expresión: hay un punto de secuencia antes deli++
y hay uno después. Por lo tanto, el único código de máquina generado es "aumentari
en1
" y está bien definido cómo se secuencia esto en relación con el resto del programa. Entonces, si lo cambiara a prefijo++
, no importaría en lo más mínimo, todavía obtendría el código de la máquina "aumentari
en1
".Las diferencias entre
++i
yi++
solo importan en expresiones comoarray[i++] = x;
versusarray[++i] = x;
. Algunos pueden argumentar y decir que el postfix será más lento en tales operaciones porque el registro dondei
reside tiene que volver a cargarse más tarde. Pero luego tenga en cuenta que el compilador es libre de ordenar sus instrucciones de la forma que desee, siempre y cuando no "rompa el comportamiento de la máquina abstracta" como lo llama el estándar C.Entonces, si bien puede suponer que
array[i++] = x;
se traduce al código de la máquina como:i
en el registro A.i
en el registro A // ineficiente porque aquí hay instrucciones adicionales, ya lo hicimos una vez.i
.el compilador también podría producir el código de manera más eficiente, como:
i
en el registro A.i
.Solo porque usted como programador en C está capacitado 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 postfix
++
en C. Ahora, lo que usted como programador en C debe variar es la gente que inconsistentemente usa el prefijo en algunos casos y postfix en otros casos, sin ninguna razón. Esto sugiere que no están seguros de cómo funciona C o que tienen un conocimiento incorrecto del lenguaje. Esto siempre es una mala señal, ya que a su vez sugiere que están tomando otras decisiones cuestionables en su programa, basadas en supersticiones o "dogmas religiosos"."El prefijo
++
siempre es más rápido" es uno de esos dogmas falsos que es común entre los posibles programadores en C.fuente
Tomando una hoja de Scott Meyers, Más eficaz c ++ Elemento 6: Distinga 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 observa el patrón de llamada de los operadores.
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 ve ejemplos que usan iteradores, siempre usan la versión de prefijo.
Pero como señala para int, efectivamente no hay diferencia debido a la optimización del compilador que puede tener lugar.
fuente
Aquí hay una observación adicional si le preocupa la micro optimización. Los bucles decrecientes pueden ser 'posiblemente' más eficientes que los bucles incrementales (dependiendo de la arquitectura del conjunto de instrucciones, por ejemplo, ARM), dado:
En cada bucle, tendrá una instrucción para:
1
ai
.i
es menor que a100
.i
es menor que a100
.Mientras que un ciclo decreciente:
El ciclo tendrá una instrucción para cada uno de:
i
, configurando el indicador de estado del registro de la CPU.Z==0
).¡Por supuesto, esto funciona solo cuando disminuye a cero!
Recordado en la Guía del desarrollador del sistema ARM.
fuente
No deje que la pregunta de "cuál 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.
Use el que tenga más sentido para el humano que lee el código.
fuente
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ápidoEn C ++,
++i
es más eficiente si fi
es algún tipo de objeto con un operador de incremento sobrecargado.¿Por qué?
En
++i
, el objeto se incrementa primero y luego puede pasar como referencia constante a cualquier otra función. Esto no es posible si la expresión sefoo(i++)
debe a que ahora se necesita hacer el incremento antes defoo()
llamarlo, pero se debe pasar el valor anteriorfoo()
. En consecuencia, el compilador se ve obligado a hacer una copiai
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ápidoSi no se necesita llamar a ningún constructor / destructor, que es siempre el caso en C,
++i
yi++
debería ser igualmente rápido, ¿verdad? No. Son prácticamente igual de rápidos, pero puede haber pequeñas diferencias, que la mayoría de los demás responden al revés.¿Cómo puede
i++
ser más rápido?El punto son las dependencias de datos. Si el valor necesita cargarse desde la memoria, se deben realizar dos operaciones posteriores con él, incrementándolo y usándolo. Con
++i
, el incremento debe hacerse antes de que se pueda usar el valor. Coni++
, 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 como máximo un ciclo de CPU, por lo que es realmente despreciable, pero está ahí. Y es al revés de lo que muchos esperarían.fuente
++i
oi++
se usa dentro de otra expresión, cambiar entre ellos cambia la semántica de la expresión, por lo que cualquier posible ganancia / pérdida de rendimiento está fuera de discusión. Si son independientes, es decir, el resultado de la operación no se usa inmediatamente, entonces cualquier compilador decente lo compilaría de la misma manera, por ejemplo, unaINC
instrucción de ensamblaje.i++
y++i
pueden ser usados indistintamente en casi todas las situaciones posibles mediante el ajuste de las constantes de bucle por uno, así que están cerca equivalente en lo que hacen para el programador. 2) Aunque ambos compilan con la misma instrucción, su ejecución difiere para la CPU. En el caso dei++
, la CPU puede calcular el incremento en paralelo con alguna otra instrucción que use el mismo valor (¡las CPU realmente hacen esto!), Mientras que con++i
la CPU tiene que programar la otra instrucción después del incremento.if(++foo == 7) bar();
yif(foo++ == 6) bar();
son funcionalmente equivalentes. Sin embargo, el segundo puede ser un ciclo más rápido, ya que la CPU puede calcular en paralelo la comparación y el incremento. No es que este ciclo único importe mucho, pero la diferencia está ahí.<
por ejemplo, vs<=
) donde++
generalmente se usa, por lo que la conversión entre las imágenes suele ser fácilmente posible.@Mark Aunque el compilador puede optimizar la copia temporal (basada en pila) de la variable y gcc (en versiones recientes) lo hace, no significa que todos los compiladores siempre lo harán.
Lo acabo de probar con los compiladores que utilizamos en nuestro proyecto actual y 3 de cada 4 no lo optimizan.
Nunca suponga 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:
Alwas prefiere ++ i sobre i ++.
fuente
En C, el compilador generalmente puede optimizarlos para que sean los mismos si el resultado no se usa.
Sin embargo, en C ++ si usa otros tipos que proporcionan sus propios operadores ++, es probable que la versión del prefijo sea más rápida que la versión postfix. Entonces, si no necesita la semántica de postfix, es mejor usar el operador de prefijo.
fuente
Puedo pensar en una situación en la que postfix es más lento que el incremento de prefijo:
Imagine que
A
se utiliza un procesador con registro como acumulador y es el único registro utilizado en muchas instrucciones (algunos microcontroladores pequeños son realmente así).Ahora imagine el siguiente programa y su traducción en un montaje hipotético:
Incremento de prefijo:
Incremento de postfix:
Observe cómo
b
se forzó la recarga del valor de . Con el incremento de prefijo, el compilador puede simplemente incrementar el valor y seguir usándolo, posiblemente evitando volver a cargarlo ya que el valor deseado ya está en el registro después del incremento. Sin embargo, con el incremento de postfix, el compilador tiene que lidiar con dos valores, uno el antiguo y otro el valor incrementado que, como muestro arriba, da como resultado un acceso más a la memoria.Por supuesto, si no se utiliza el valor del incremento, como una sola
i++;
declaración, el compilador puede (y lo hace) simplemente generar una instrucción de incremento independientemente del uso de prefijo o postfix.Como nota al margen, me gustaría mencionar que una expresión en la que hay un
b++
no puede convertirse simplemente en una++b
sin ningún esfuerzo adicional (por ejemplo, agregando a- 1
). Entonces, comparar los dos si son parte de alguna expresión no es realmente válido. A menudo, cuando usab++
dentro de una expresión que no puede usar++b
, por lo que incluso si++b
fuera potencialmente más eficiente, simplemente estaría mal. La excepción es, por supuesto, si la expresión lo está pidiendo (por ejemplo,a = b++ + 1;
que se puede cambiar aa = ++b;
).fuente
He estado leyendo a través de la mayor parte de las respuestas aquí y muchos de los comentarios, y yo no vi ninguna referencia a la una instancia que lo que podía pensar en que
i++
es más eficiente que++i
(y tal vez sorprendentemente--i
era más eficiente quei--
). ¡Eso es para los compiladores de C para el DEC PDP-11!El PDP-11 tenía instrucciones de montaje para el decremento previo de un registro y el incremento posterior, pero no al revés. Las instrucciones permitieron que cualquier registro de "propósito general" se utilizara como un puntero de pila. Entonces, si usó algo así
*(i++)
, podría compilarse en una sola instrucción de ensamblaje, mientras*(++i)
que no podría.Obviamente, este es un ejemplo muy esotérico, pero proporciona la excepción donde el incremento posterior es más eficiente (o debería decir que lo fue , ya que no hay mucha demanda de código PDP-11 C en estos días).
fuente
--i
yi++
.Siempre prefiero pre-incremento, sin embargo ...
Quería señalar que incluso en el caso de llamar a la función operator ++, el compilador podrá optimizar lo temporal si la función se alinea. Dado que el operador ++ suele ser corto y a menudo implementado en el encabezado, es probable que se inserte.
Entonces, para fines prácticos, es probable que no haya mucha diferencia entre el rendimiento de las dos formas. Sin embargo, siempre prefiero el incremento previo, ya que parece mejor expresar directamente lo que estoy tratando de decir, en lugar de depender del optimizador para resolverlo.
Además, darle menos posibilidades al optimizador significa que el compilador se ejecuta más rápido.
fuente
Mi C está un poco oxidada, así que me disculpo de antemano. Speedwise, puedo entender los resultados. Pero, estoy confundido sobre cómo ambos archivos salieron al mismo hash MD5. Tal vez un bucle for se ejecuta igual, pero ¿las siguientes 2 líneas de código no generarían un ensamblaje diferente?
vs
El primero escribe el valor en la matriz, luego incrementa i. Los segundos incrementos luego escribo en la matriz. No soy un experto en ensamblaje, pero simplemente no veo cómo se generaría el mismo ejecutable con estas 2 líneas de código diferentes.
Solo mis dos centavos.
fuente
foo[i++]
afoo[++i]
sin cambiar nada más obviamente cambiaría la semántica del programa, pero en algunos procesadores cuando se usa un compilador sin una lógica de optimización de elevación de bucle, incrementandop
yq
una vez y luego ejecutando un bucle que funciona, por ejemplo*(p++)=*(q++);
, sería más rápido que usar un bucle que funciona*(++pp)=*(++q);
. Para bucles muy ajustados en algunos procesadores, la diferencia de velocidad puede ser significativa (más del 10%), pero ese es probablemente el único caso en C donde el incremento posterior es materialmente más rápido que el incremento previo.