2012-10-10 31 views
5
  1. ¿Hay una diferencia entre los dos bloques de código siguientes en términos del código de máquina resultante al usar los compiladores llvm o gcc?
  2. ¿Cuándo esta optimización realmente vale la pena, si es que alguna vez?

no está optimizada:¿Vale la pena precomputar el condicional en un bucle for?

for (int i=0; i<array.count; i++) { 
    //do some work 
} 

optimizada:

int count = array.count; 
for (int i=0; i<count; i++) { 
    //do some work 
} 

EDITAR: Debo señalar que array es inmutable y array.count no cambia durante la ejecución del bucle.

+2

A menudo no es necesario ya que el compilador probablemente hará esa optimización de todos modos. Pero la única forma de estar seguro es probar ambas formas y medir. En cuanto al código de máquina resultante, puede verificar fácilmente que tanto clang como gcc tienen indicadores para mostrar archivos de ensamblador, y siempre podría desmontar el ejecutable si nada más. –

+1

En este caso, hay un mensaje enviado a un objeto, dudo que el compilador interfiera con él, así que supongo que se llamará al método 'count' en cada iteración. Sin embargo, es probable que sea una llamada muy barata. La mejor manera en este caso particular sería usar una enumeración rápida en la matriz. – Guillaume

+3

"la matriz es inmutable y array.count no cambia" - No sé Objective-C, si "inmutable" es una propiedad que el compilador entiende, pero para fines de optimización no importa si ' cambios en array.count', importa si el compilador puede probarse a sí mismo que 'array.count' no cambia. –

Respuesta

3
  1. que realmente necesita para hacer que lo busque. Supongo que es una diferencia en el código emitido, pero podría depender de las opciones del compilador y compilador, y ciertamente puede depender de la definición de array.
  2. Casi nunca, en el supuesto de que evaluar array.count es casi siempre insignificante en comparación con "algún trabajo". La forma de medirlo, sin embargo, es utilizar un generador de perfiles (o equivalente) y observar qué proporción del tiempo de ejecución de su programa se utiliza en esa línea de código. Siempre que el generador de perfiles sea preciso, esa es la más que podría esperar al cambiarla.

Supongamos que array.count es algo realmente lento, que usted sabe que siempre devolverá el mismo resultado pero el compilador no lo sabe. Entonces podría valer la pena izarlo manualmente. strlen se usa como ejemplo. Es discutible la frecuencia strlen es realidad lenta en la práctica, pero fácil de fabricar ejemplos que puedan funcionar más lentamente de lo que necesitan:

char some_function(char a) { 
    return (a * 2 + 1) & 0x3F; 
} 

for (int i = 0; i < strlen(ptr); ++i) { 
    ptr[i] = some_function(ptr[i]); // faster than strlen for long enough strings. 
} 

Usted y yo sabemos que some_function nunca se devuelve 0, y por tanto la longitud de la la cadena nunca cambia Es posible que el compilador no vea la definición de some_function, e incluso si lo hace, es posible que la definición no se dé cuenta de que su recuperación no nula es importante.

1

La respuesta de Steve Jessop es buena. Solo quiero agregar:

Personalmente, siempre utilizo la versión optimizada. Solo en mi conjunto de buenas prácticas elimino cada componente constante del ciclo. No es mucho trabajo y hace que el código sea más limpio. No es una "optimización prematura" y no presenta ningún problema o compensación. Hace que la depuración sea más fácil (paso a paso). Y podría hacer que el código sea más rápido. Por lo tanto, es obvio para mí.