2011-10-07 8 views
9

Cuando un compilador realiza una optimización de desenrollado en bucle, ¿cómo determina qué factor desenrollar el bucle o si desenrollar todo el bucle? Dado que se trata de una compensación de desempeño espacial, en promedio, ¿cuán eficiente es esta técnica de optimización para que el programa tenga un mejor rendimiento? Además, ¿en qué condiciones se recomienda utilizar esta técnica (es decir, ciertas operaciones o cálculos)?¿Cómo la optimización de compiladores decide cuándo y cuánto desenrollar un ciclo?

Esto no tiene por qué ser específico de un compilador determinado. Puede ser cualquier explicación que describa la idea detrás de esta técnica y lo que se ha observado en la práctica.

+11

¿Está buscando un documento sobre el análisis de optimización del compilador? :) – Jon

+1

Me gustaría agregar: ¿por qué el mensaje de ayuda de gcc dice -funroll-all-loops en realidad hace que el programa se ejecute más lento? Citando: "Realice la optimización de desenrollado del bucle. Esto se realiza para todos los bucles y generalmente hace que los programas se ejecuten más lentamente". – BlackBear

+0

@Jon, no importa, solo necesito una buena respuesta. –

Respuesta

8

Cuando un compilador realiza una optimización de desenrollado de bucle, ¿cómo se determina por qué factor para desenrollar el bucle o el clima para desenrollar el bucle completo o no?

consumo de la pila y localidad. la instrucción cuenta capacidad de hacer/propagar optimizaciones basadas en el programa desenrollado y en línea. si el tamaño del bucle es fijo, o se espera que esté en un cierto rango. entradas de perfil (si corresponde). operaciones que pueden ser eliminadas del cuerpo del bucle. etc.

Dado que esto es una compensación del rendimiento del espacio en promedio, ¿qué tan efectiva es esta técnica de optimización para que el programa tenga un mejor rendimiento?

depende en gran medida de la entrada (su programa). puede ser más lento (no típico) o puede ser varias veces más rápido. se aprende a escribir un programa para ejecutar de manera óptima y que también permite que el optimizador haga su trabajo.

También, bajo qué condiciones es recomendable utilizar esta técnica (es decir, ciertas operaciones o cálculos)

generalmente, un gran número de iteraciones en cuerpos muy pequeños, en particular lo que es sin sucursales y tiene buena ubicación de datos.

si desea saber si la opción ayuda a su aplicación, perfil.

si necesita más que eso, debe reservar algo de tiempo para aprender a escribir programas óptimos, ya que el tema es bastante complejo.

+0

¿Tiene alguna recomendación de recursos para escribir programas óptimos? –

+0

realmente depende de su nivel de conocimiento actual y de los programas que escribe ... quizás encuentre este un buen recurso: http://www.agner.org/optimize/ – justin

+0

+1 Para el enlace Justin. Encontré este bit en los foros de MASM para ser divertidamente duro: "No para los débiles de corazón. Si MASM está más allá de ti, utiliza las secuencias de comandos del lado del servidor". –

1

cuando es (en mi opinión) buena para desenrollar un bucle:

bucle es corto y posiblemente todas las variables utilizadas son de registro del procesador. Después de desenrollar las variables están 'duplicadas', pero aún están en registros por lo que no hay penalización de memoria (o caché).

El bucle (con el número de unrool de bucle desconocido) se ejecutará al menos unas pocas o docenas de veces, por lo que hay justificación para cargar ese bucle completo desenrollado en la memoria caché de instrucciones.

si el ciclo es corto (una o unas pocas instrucciones) puede ser muy beneficioso para el desenrollado porque el código para determinar si debe ejecutarse nuevamente se ejecuta con menos frecuencia.

3

El análisis simplista consiste en contar instrucciones: un bucle de 2 instrucciones desenrollado 10 veces tiene 11 instrucciones en lugar de 20 produce una aceleración de 11/20. Pero con arquitecturas de procesador modernas es mucho más complejo; dependiendo de los tamaños de caché y las características de la cartera de instrucciones de los procesadores. Es posible que el ejemplo anterior se ejecute 10 veces más rápido en lugar de 2x. También es posible que desenrollar 1000x en vez de 10x sea más lento. Sin dirigirse a un procesador específico, los compiladores (o pragmas que usted escribe para ellos) simplemente están adivinando.

1

Ok, antes que nada, no sé cómo los compiladores lo hacen automáticamente. Y estoy bastante seguro de que hay al menos 10s si no cientos de algoritmos de los que los compiladores tienen que elegir.
Y, sin embargo, es probablemente un compilador específico.

Pero puedo ayudarlo a calcular su efectividad.

Solo tenga en cuenta que esta técnica por lo general no le da un gran aumento de rendimiento.
Pero en cálculos repetidos en bucle y puede dar un alto porcentaje de rendimiento.
Esto se debe a que, por lo general, la función dentro del ciclo requiere mucho más tiempo de cálculo que la verificación de estado del ciclo.

Por lo tanto, digamos que tenemos un bucle simple con una constante, porque estabas demasiado perezosos para hacer copiar y pegar o simplemente pensó que se vería mejor:

for (int i = 0; i < 5; i++) 
{ 
    DoSomething(); 
} 

Aquí tienes comparaciones int , Incrementaciones, y DoSomethig() llamadas.
Entonces, si DoSomething() es relativamente rápido, entonces obtuvimos operaciones.
Ahora, si desenrolla esto, se va a reducir a sólo 5 operaciones:

DoSomething(); 
DoSomething(); 
DoSomething(); 
DoSomething(); 
DoSomething(); 

Ahora, con constantes Es más fácil, por lo que permite ver cómo funcionaría con una variable:

for (int i = 0; i < n; i++) 
{ 
    DoSomething(); 
} 

Aquí tienen n comparaciones INT, n incrementations y n DoSomethig() llama = 3n . Ahora bien, no se puede desenrollar por completo, pero podría desenrollarlo por un factor constante (cuanto más alto se espera n a ser, más debemos desenrollarlo):

int i; 
for (i = 0; i < n; i = i+3) 
{ 
    DoSomething(); 
    DoSomething(); 
    DoSomething(); 
} 
if (i - n == 2) 
{ 
    DoSomething(); // We passed n by to, so there's one more left 
} 
else if (i - n == 1) 
{ 
    DoSomething(); //We passed n by only 1, so there's two more left 
    DoSomething(); 
} 

Ahora aquí tenemos aquí tienes n/3 + 2 comparaciones int, n/3 incrementations y n DoSomethig() llama a = (1 2/3) * n.
Nos hemos ahorrado (1 1/3) * n operaciones. Lo que reduce el tiempo de cálculo casi a la mitad.

FYI, otra técnica de desenrollado ordenada se llama Duff's device.
Pero es muy específico de compilación e implementación de lenguaje. Hay idiomas donde esto sería peor.

Cuestiones relacionadas