2008-09-10 16 views
6

¿Qué compila para un código más rápido: "ans = n * 3" o "ans = n + (n * 2)"?¿Qué compila para un código más rápido: "n * 3" o "n + (n * 2)"?

Suponiendo que n es un int o un long, y se está ejecutando en un moderno Win32 Intel box.

¿Sería esto diferente si hubiera alguna desreferencia involucrada, es decir, cuál de estas sería más rápida?

 

long a; 
long *pn; 
long  ans; 

... 
*pn = some_number; 
ans = *pn * 3; 

O

 
ans = *pn+(*pn*2); 

O, ¿es algo que uno no tiene que preocuparse de que los compiladores de optimización son propensos a dar cuenta de esto en cualquier caso?

Respuesta

55

IMO tal micro-optimización no es necesaria a menos que trabaje con algún compilador exótico. Pondría legibilidad en primer lugar.

1

Depende del compilador que está usando en realidad, pero es muy probable que traduzcan al mismo código.

Puede comprobarlo usted mismo creando un pequeño programa de prueba y comprobando su desmontaje.

1

La mayoría de los compiladores son lo suficientemente inteligentes como para descomponer una multiplicación de enteros en una serie de cambios de bit y agrega. No sé acerca de los compiladores de Windows, pero al menos con gcc puede lograr escupir al ensamblador, y si observa eso, probablemente pueda ver el ensamblador idéntico para ambas formas de escritura.

0

Los compiladores son buenos para optimizar código como el suyo. Cualquier compilador moderno produciría el mismo código para ambos casos y adicionalmente reemplazaría * 2 por un cambio a la izquierda.

+0

No estés tan seguro :) Vi algunos compiladores realmente extraños para el desarrollo de software integrado. – aku

+1

En los sistemas integrados, casi todo el conocimiento convencional termina. ;-) –

4

Esto dependería del compilador, su configuración y el código que lo rodea.

No debe intentar adivinar si las cosas son "más rápidas" sin tomar medidas.

En general usted no debe preocuparse por este tipo de nanoescala cosas optimización de hoy en día - es casi siempre una completa irrelevancia, y si estuviera realmente trabajando en un dominio donde importaba, ya estaría utilizando un generador de perfiles y mirando en el resultado del lenguaje ensamblador del compilador.

10

Como es fácil de medir usted mismo, ¿por qué no hacer eso? (El uso de gcc y time de cygwin)

/* test1.c */ 
int main() 
{ 
    int result = 0; 
    int times = 1000000000; 
    while (--times) 
     result = result * 3; 
    return result; 
} 

machine:~$ gcc -O2 test1.c -o test1 
machine:~$ time ./test1.exe 

real 0m0.673s 
user 0m0.608s 
sys  0m0.000s 

hacer la prueba para un par de veces y repita para el otro caso.

Si quieres echar un vistazo al código ensamblador, gcc -S -O2 test1.c

+0

Desafortunadamente este es un mal ejemplo: con i686-apple-darwin8-gcc-4.0.1, elimina por completo el "resultado = resultado * 3" del bucle, ya que siempre es cero. Cambiar la condición inicial a "resultado = 1" da un mejor resultado. –

+0

o mejor, cree una matriz de números aleatorios y procese eso, de modo que el compilador no pueda hacer suposiciones. – DarenW

15

no importa. Los procesadores modernos pueden ejecutar una instrucción MUL entera en un ciclo de reloj o menos, a diferencia de los procesadores anteriores que necesitaban realizar una serie de cambios y los agregaban internamente para realizar el MUL, por lo tanto, usaban ciclos múltiples.Apostaría que

MUL EAX,3 

ejecuta más rápido que

MOV EBX,EAX 
SHL EAX,1 
ADD EAX,EBX 

El último procesador, donde este tipo de optimización podría haber sido útil fue probablemente el 486. (sí, esto es sesgada a los procesadores de Intel, pero es probablemente representativo de otras arquitecturas también).

En cualquier caso, cualquier compilador razonable debería poder generar el código más pequeño/más rápido. Así que siempre ve con facilidad de lectura primero.

+3

Realmente dudo que el MUL se ejecute más rápido, una vez que se tiene en cuenta la latencia y la inflexibilidad sobre qué registros puede usar. Además, en x86, LEA, no en la secuencia de 3 instrucciones que proporcionó, sería utilizado por cualquier compilador decente tanto para 3 * n como para n + 2 * n. –

+1

Es cierto, pero LEA solo es útil cuando se multiplica por un pequeño conjunto de constantes (2, 3, 4, 5, 8 y 9 si recuerdo correctamente). De todos modos, mi punto era dejar que el compilador descubriera el código más rápido. – Ferruccio

4

No es difícil averiguar qué está haciendo el compilador con su código (estoy usando DevStudio 2005 aquí). Escribir un programa sencillo con el siguiente código:

int i = 45, j, k; 
j = i * 3; 
k = i + (i * 2); 

Coloque un punto de interrupción en la línea media y ejecutar el código con el depurador. Cuando se desencadena el punto de interrupción, haga clic derecho en el archivo de origen y seleccione "Ir al desmontaje". Ahora tendrá una ventana con el código que está ejecutando la CPU. En este caso, notará que las dos últimas líneas producen exactamente las mismas instrucciones, es decir, "lea eax, [ebx + ebx * 2]" (no se trata de un cambio de bit y se agrega en este caso particular). En una CPU IA32 moderna, probablemente sea más eficiente hacer un MUL directo en lugar de un cambio de bit debido a la naturaleza de la CPU que incurre en una penalización al usar un valor modificado demasiado pronto.

Esto demuestra de lo que está hablando aku, es decir, los compiladores son lo suficientemente inteligentes como para elegir las mejores instrucciones para su código.

+0

No conozco la tubería, es un problema. La unidad aritmética probablemente tenga hadle ebx + ebx * 2 internamente en un solo paso. – Artelius

0

Confíe en su compilador para optimizar pequeños códigos de ese tipo. La legibilidad es mucho más importante en el nivel de código. La verdadera optimización debería llegar a un nivel superior.

1

No le importa. Creo que hay cosas más importantes que optimizar. ¿Cuánto tiempo ha invertido pensando y escribiendo esa pregunta en lugar de codificar y probar usted mismo?

:-)

1

Mientras estás utilizando un compilador de optimización decente, justo escribir código que es fácil para el compilador de entender. Esto hace que sea más fácil para el compilador realizar optimizaciones inteligentes.

Si hace esta pregunta, indica que un compilador de optimización sabe más acerca de la optimización que usted. Así que confía en el compilador. Use n * 3.

Echa un vistazo a this answer también.

Cuestiones relacionadas