Compilador Diseño - Optimización de Código



Optimización es un programa transformación técnica, que trata de mejorar el código por lo que consumen menos recursos (es decir CPU, memoria) y ofrecer una alta velocidad.

En la optimización de alto nivel general de programación son sustituidos por construcciones muy eficiente de bajo nivel los códigos de programación. Un código proceso en fase de optimización debe seguir las tres normas que se explican a continuación:

  • El código de salida no debe, de ninguna manera, cambiar el sentido del programa.

  • Optimización debe aumentar la velocidad del programa y si es posible, el programa debe exigir menos cantidad de recursos.

  • Optimización debe ser rápido y no debe retrasar el proceso de compilación general.

Los esfuerzos para un código optimizado puede ser utilizado en los distintos niveles de elaboración del proceso.

  • Al principio, los usuarios pueden cambiar o reorganizar el código o utilizar las mejores algoritmos para escribir el código.

  • Después de generar código intermedio, el compilador puede modificar el código intermedio por dirección los cálculos y mejorar los lazos.

  • Al tiempo que se produce la máquina de destino código, el compilador puede hacer uso de jerarquía de memoria y registros de la CPU.

La optimización puede clasificarse en dos grandes categorías: independiente de la máquina y depende de la máquina.

Optimización independiente de la máquina

En esta optimización, el compilador toma en el código intermedio y transforma una parte del código que no implique un registros de la CPU y/o ubicaciones de memoria absoluta. Por ejemplo:

do
{
   item = 10;
   value = value + item; 
} while(value<100);

Este código implica repetir la asignación de elemento identificador, que si ponemos esta forma:

Item = 10;
do
{
   value = value + item; 
} while(value<100);

No sólo debe guardar los ciclos de la CPU, pero puede ser utilizada en cualquier procesador.

Optimización dependientes de la máquina

Dependientes de La Máquina optimización se realiza después de que el código de destino se ha generado y cuando el código se transforman de acuerdo a la arquitectura del equipo de destino. Registros de la CPU se trata y puede tener referencias de memoria absoluta en lugar de referencias relativas. Optimizadores dependientes de la máquina que los esfuerzos para aprovechar al máximo de jerarquía de memoria.

Bloques básicos

Por lo general, los códigos fuente tienen una serie de instrucciones que se ejecutan siempre en orden y están consideradas como los bloques básicos del código. Estos dos bloques básicos no tienen instrucciones de salto entre ellos, es decir, cuando la primera se ejecuta la instrucción, todas las instrucciones en el mismo bloque básico será ejecutado en su secuencia de aparición sin perder el control de flujo del programa.

Un programa puede tener diversas construcciones como bloques básicos, como IF-THEN-ELSE, SWITCH-CASE las sentencias condicionales, bucles, como DO-WHILE, FOR y REPETIR HASTA QUE, etc.

Identificación del bloque básico

Podemos utilizar el siguiente algoritmo para encontrar los bloques básicos en un programa:

  • Declaraciones del cabezal Búsqueda de todos los bloques básicos desde donde se inicia un bloque básico:

    • Primera declaración de un programa.
    • Las declaraciones que son objetivo de cualquier rama (condicional o incondicional).
    • Las declaraciones que siguen cualquier rama.
  • Las declaraciones del cabezal y las declaraciones siguientes forman un bloque básico.

  • Un bloque básico no incluye cualquier cabezal declaración de cualquier otro bloque básico.

Bloques básicos son conceptos importantes de generación de código y optimización punto de vista.

Bloques básicos

Bloques básicos desempeñan un papel importante para identificar las variables, que se están utilizando más de una vez en un único bloque básico. Si cualquier variable se utiliza más de una vez, el registro memoria asignada a la variable no es necesario vaciar el bloque a menos que termine la ejecución.

Flujo de Control Gráfico

Bloques básicos en un programa puede ser representado por medio de gráficos de flujo de control. Un flujo de control gráfico se muestra cómo el control del programa se pasa entre los bloques. Es una herramienta útil que ayuda en la optimización por ayudar a localizar cualquier bucles no deseados en el programa.

Control de Flujo Gráfico

Optimización Bucle

La mayoría de los programas se ejecutan como un bucle en el sistema. Se hace necesario optimizar los lazos con el fin de ahorrar ciclos de CPU y memoria. Los loops pueden ser optimizados por las siguientes técnicas:

  • Código invariante: un fragmento de código que reside en el bucle y calcula el mismo valor en cada iteración se denomina bucle de código invariante. Este código puede ser trasladado fuera del circuito de ahorro que se calculan sólo una vez, en lugar de en cada iteración.

  • Inducción análisis: una variable se denomina inducción variable si su valor es modificado en el bucle de un bucle de valor invariable.

  • Fuerza reducción: Hay expresiones que consumen más ciclos de CPU, el tiempo y la memoria. Estas expresiones deben reemplazarse con las expresiones más baratos sin comprometer el resultado de expresión. Por ejemplo, la multiplicación (x * 2) es costosa en términos de ciclos de CPU que (x << 1) y el mismo resultado.

Eliminación de código muerto

Código Muerto es uno o más de un código, que son:

  • O nunca ejecutados o inalcanzable,
  • O si se ejecuta, su producción nunca se utiliza.

Por lo tanto, código muerto juega papel alguno en cualquier operación del programa y que, por lo tanto, puede eliminarse simplemente.

Código muerto parcialmente

Hay algunas instrucciones de código cuyo valores calculados se utilizan sólo en determinadas circunstancias, es decir, a veces se utilizan los valores y a veces no lo son. Estos códigos son conocidos como parte de código.

Código Parcialmente Muerto

El flujo de control gráfico se muestra un fragmento de programa donde variable "a" se utiliza para asignar la salida de expresión ‘x * y’. Supongamos que el valor asignado a 'a' nunca se utiliza dentro del bucle.Inmediatamente después de la salida del bucle de control, 'a' se le asigna el valor de la variable ‘z’, que se utilizarán posteriormente en el programa. Podemos concluir que el código de asignación de la letra 'a' nunca es utilizado en cualquier parte, por lo tanto, es elegible para ser eliminado.

Código Muerto

Del mismo modo, la imagen de arriba muestra que la instrucción condicional siempre es falso, lo que implica que el código, escrito en cierto caso, nunca se ejecutará, por lo que se puede quitar.

Redundancia Parcial

Las expresiones redundantes se calculan más de una vez en ruta paralela, sin ningún cambio de operandos.mientras que parcial de las expresiones redundantes se calculan más de una vez en el camino, sin ningún cambio de operandos. Por ejemplo,

Expresión Redundante

Expresión] [redundante

Expresión parcialmente redundante

[Parcialmente redundante expresión]

Loop-invariante código es parcialmente redundante y pueden ser eliminados mediante un código de movimiento técnica.

Otro ejemplo de código parcialmente redundante puede ser:

If (condition)
{
   a = y OP z;
}
else
{
   ...
}
c = y OP z;

Vamos a asumir que los valores de los operandos (y Y z) no se cambian de asignación de variable a variable c. Aquí, si la condición es verdadera, y OP z se calcula dos veces, si no, una vez. Movimiento código se puede utilizar para eliminar esta redundancia, como se muestra a continuación:

If (condition)
{
   ...
   tmp = y OP z;
   a = tmp;
   ...
}
else
{
   ...
   tmp = y OP z;
}
c = tmp;

En este caso, si la condición es verdadera o falsa; y OP z debe calcularse sólo una vez.

Advertisements