miércoles, 13 de noviembre de 2013

TIPOS DE OPTIMIZACION

TAREA_8_FERNANDEZ
OPTMIZACION

La optimización busca mejorar la forma en que un programa utiliza los recursos. Las optimizaciones se realizan en base al alcance ofrecido por el compilador. La optimización va a depender del lenguaje de programación y es directamente proporcional al tiempo de compilación; es decir, entre más optimización mayor tiempo de compilación.
Existen diversas técnicas de optimización se pueden clasificar o dividir de diversas formas:
1)      Dependientes de la maquina: técnicas que solo se pueden aplicar a una determinada maquina objeto.
2)     Independientes de la maquina: técnicas que son aplicables a cualquier maquina objeto.
3)      Locales: analizaran solo pequeñas porciones de código y en ellas realizaran mejoras.
4)     Globales: será necesario el análisis de todo el código.

TIPOS DE OPTIMIZACION
Técnicas de optimización que se aplican al código generado para un programa sencillo (aquel que se reduce a un solo procedimiento o subrutina).

1-  LOCALES
La optimización local se realiza sobre módulos del programa. En la mayoría de las ocasiones a través de funciones, métodos, procedimientos, clases, etc.
Las características de las optimizaciones locales es que solo se ven reflejados en dichas secciones. La optimización local sirve cuando un bloque de programa o sección es crítico por ejemplo: la E/S, la concurrencia, la rapidez y confiabilidad de un conjunto de instrucciones.

EJEMPLOS:

1-      Ejecución en tiempo de compilación
Precalcular  expresiones constantes (con constantes o variables cuyo valor no cambia).
3 ! i = 5
j = 4
f = j + 2.5
!
j = 4
f = 6.5

2-     Reutilización de expresiones comunes
a = b + c
d = a - d
e = b + c
f = a - d
!
a = b + c
d = a - d
e = a
f = a – d

3-     Propagación de copias
Ante instrucciones f=a, sustituir todos los usos de f por a.
a = 3 + i
f = a
b = f + c
d = a + m
m = f + d
!
a = 3 + i
b = a + c
d = a + m
m = a + d

4-     Eliminación redundancias en acceso matrices
Localizar expresiones comunes en cálculo direcciones de matrices.

5-     Transformaciones algebraicas:
Aplicar propiedades matemáticas para simplificar expresiones

o   Eliminación secuencias nulas
o   Reducción de potencia
o   Reacondicionamiento de operandos

2- CICLOS

Los ciclos son una de las partes más esenciales en el rendimiento de un programa dado que realizan acciones repetitivas, y si dichas acciones están mal realizadas, el problema se hace N veces más grandes. La mayoría de las optimizaciones sobre ciclos tratan de encontrar elementos que no deben repetirse en un ciclo.
El problema de la optimización en ciclos y en general radica en que es muy difícil saber el uso exacto de algunas instrucciones. Así que no todo código de proceso puede ser optimizado. Otro uso de la optimización puede ser el mejoramiento de consultas en SQL o en aplicaciones remotas (sockets, E/S, etc.).

3-MIRILLA

La optimización de mirilla trata de estructurar de manera eficiente el flujo del programa, sobre todo en instrucciones de bifurcación como son las decisiones, ciclos y saltos de rutinas. La idea es tener los saltos lo más cerca de las llamadas, siendo el salto lo más pequeño posible.
Ideas básicas:

  • Se recorre el código buscando combinaciones de instrucciones que pueden ser reemplazadas por otras equivalentes más eficientes.
  • Se utiliza una ventana de n instrucciones y un conjunto de patrones de transformación (patrón, secuencias, remplazan).
  • Las nuevas instrucciones son reconsideradas para las futuras optimizaciones.
Ejemplos:

  • Eliminación de cargas innecesarias
  • Reducción de potencia
  • Eliminación de cadenas de saltos

CONCLUSION

El objetivo de las técnicas de optimización es mejorar el programa objeto para que nos dé un rendimiento mayor. La mayoría de estas técnicas vienen a compensar ciertas ineficiencias que son inherentes al concepto de lenguaje de alto nivel, el cual suprime detalles de la maquina objeto para facilitar la tarea de implementar un algoritmo.

jueves, 31 de octubre de 2013

GENERACION DE CODIGO INTERMEDIO

TAREA_7_FERNANDEZ
 
El proceso de la compilación se desglosa en dos partes: la primera corresponde  con la parte de análisis (léxico, sintáctico y semántico). La segunda corresponde con la parte de síntesis (generación de código). Este último es la tarea más complicada de un compilador.

Una representación intermedia es una estructura de datos que representa al programa fuente durante el proceso de la traducción a código objeto. El árbol de análisis sintáctico como representación intermedia, junto con la tabla de símbolos que contenía información sobre los nombres (variables, constantes, tipos y funciones) que aparecían en el programa fuente.

Es necesario generar una nueva forma de representación intermedia. A esta representación intermedia, que se parece al código objeto pero que sigue siendo independiente de la máquina, se le llama código intermedio. Este puede tomar muchas formas. Todas ellas se consideran como una forma de linearizacion del árbol sintáctico, es decir, una representación del árbol sintáctico de forma secuencial. El código intermedio más habitual es el código de 3-direcciones este es una secuencia de proposiciones de la forma general  X= Y op Z:

DONDE:
 OP representa cualquier operador; X, Y, Z, operador aritmético de punto fijo o flotante, o un operador lógico sobre datos booleanos. Representan variables definidas por el programador o variables temporales generadas por el compilador. Y, Z también puede representar constantes o literales. 

Se llama código de 3-direcciones porque cada proposición contiene, en el caso general, tres direcciones, dos para los operandos y una para el resultado. (Aunque aparece el nombre de la variable, realmente corresponde al puntero a la entrada de la tabla de símbolos de dicho nombre).

El conjunto de proposiciones (operadores) debe ser lo suficientemente rico como para poder implantar las operaciones del lenguaje fuente. Las proposiciones de 3-direcciones van a ser en cierta manera análogas al código ensamblador.

IMPEMENTACION DE CODIGO DE TRES DIRECCIONES
Una proposición de código de 3-direcciones se puede implantar como una estructura tipo registro con campos para el operador, los operandos y el resultado. La representación final será entonces una lista enlazada o un vector de proposiciones.

IMPLEMENTACION MEDIANTE CUADRUPLOS
Un cuádruplo es una estructura tipo registro con cuatro campos(op, result, arg1, arg2). El campo op contiene un código interno para el operador.
Una alternativa a almacenar nombres en el cuádruplo es almacenar punteros a la tabla de símbolos, con lo que se evita tener que hacer operaciones de búsqueda en un procesado posterior.

IMPLEMENTACION MEDIANTE TRIPLETES
Para evitar tener que introducir nombres temporales en la tabla de símbolos, se hace referencia a un valor temporal según la posición de la proposición que lo calcula. Las propias instrucciones representan el valor del nombre temporal. La implantación se hace mediante registros de solo tres campos (op, arg1, arg2).

En la notación de tripletes se necesita menor espacio y el compilador no necesita generar los nombres temporales. Sin embargo, en esta notación, trasladar una proposición que defina un valor temporal exige que se modifique todas las referencias a esa proposición. Lo cual supone un inconveniente a la hora de optimizar el código, pues a menudo es necesario cambiar proposiciones de lugar.

CODIGO INTERMEDIO COMO UN ATRIBUTO SINTETIZADO
El código intermedio es visto como una cadena de caracteres y se puede diseñar un esquema de traducción dirigido por la sintaxis (ETDS) que genere dicho código al recorrer el árbol de análisis sintáctico en orden posfijo.


REUTILIZACION DE LOS NOMBRES TEMPORALES
Los nombres temporales utilizados para guardar valores intermedios de los cálculos de expresiones tienen que ser introducidos en la TS para guardar sus valores con el consiguiente aumento de espacio. Los nombres temporales se pueden reutilizar mediante un sencillo método que consiste en llevar una cuenta c, iniciada cero, de variables temporales. Cuando se utilice un nombre temporal como operando hay que decrementar el valor de c en 1.  

TRADUCCION DE EXPRESIONES BOOLEANAS
Las expresiones booleanas se utilizan principalmente como parte de las proposiciones condicionales que alteran el flujo de control del programa, if-then, if-then-else, while-do. Las expresiones booleanas se componen de los operadores booleanos and, or, not aplicados a variables booleanas o expresiones relacionales.

Uno de los métodos para traducir expresiones booleanas a código de 3-direcciones consiste en codificar numéricamente los valores true y false y evaluar una expresión booleana como una expresión aritmética, siguiendo unas prioridades. A menudo se utilizan 1 para indicar true y 0 para indicar false. Las expresiones booleanas se evalúan de manera similar a una expresión aritmética de izquierda a derecha.

TRADUCCION DE FUNCIONES
La generación de código para las funciones es la parte más complicada porque depende de la maquina objeto y de la organización del entorno de ejecución. La traducción de funciones implica dos etapas:

  •  La definición de la función: esta crea un nombre, parámetros y código del cuerpo de la función pero no se ejecuta la función en ese punto.

  • La llamada a la función: esta llamada crea los valores actuales para los parámetros y realiza un salto al código de entrada de la función que se ejecuta y retorna al punto de la llamada.
OPTIMIZACION DE CODIGO INTERMEDIO
La optimización de código intermedio se puede realizar:

  •   A nivel local: solo utilizan la información de un bloque básico para realizar la optimización.
  •   A nivel global: que usan información de varios bloques básicos.

El término de MEJORA de código sería más apropiado que el de OPTIMIZACIÓN.
La mayoría de los programas emplean el 90% del tiempo de ejecución en el 10% de su código. Lo más adecuado es identificar las partes del programa que se ejecuten más frecuentemente y tratar de que se ejecuten lo más eficientemente posible. Además de la optimización a nivel de código intermedio, se puede reducir el tiempo de ejecución de un programa actuando a otros niveles: a nivel de código fuente y a nivel de código objeto.
Un bloque básico es una unidad fundamental de código, es una secuencia de proposiciones donde el flujo de control entre en el principio del bloque y sale al final del bloque. Un flujo de control puede visualizarse como un grafo dirigido de bloques básicos.

ELIMINACION DE SUBEXPRESIONES COMUNES
Si una expresión se calcula más de una vez, se puede remplazar el cálculo de la segunda por el valor de la primera expresión. Esto solo es posible si los operandos que están implicados en el cálculo de la expresión no han modificado su valor en las proposiciones intermedias.

PROPAGACION DE COPIAS
La propagación de copias considera las proposiciones de la forma α = b. Después de esta sentencia sabemos que α y b tienen el mismo valor, por tanto, podemos remplazar cada vez que aparezca α por b, con la esperanza de que se pueda remplazar todas las ocurrencias de α hasta que se convierta en un nombre muerto y se pueda entonces eliminar la proposición de copia.

ELIMINACION DE CODIGO MUERTO
Podemos tener proposiciones que definen un nombre que nunca más es referenciado, está muerto. Estas proposiciones pueden entonces ser eliminadas. Dada la proposición α = b op c, se dice que es código muerto o inactivo si α no es referenciada. En general, el código muerto aparece como consecuencia de la propagación de copias y es esto lo que hace que la técnica de propagación de copias sea tan útil.

TRANSFORMACIONES ARITMETICAS
Se pueden hacer uso de transformaciones algebraicas simples para reducir la cantidad de computación transformando operaciones mas costosas por otras menos costosas. Existen tres tipos:

·         Calculo previo de constantes
Se trata de calcular a nivel de compilación el valor previo de constantes en vez de hacerlo en tiempo de ejecución que retardaría la ejecución de un programa.
·         Transformaciones algebraicas
Se puede hacer uso de identidades algebraicas para simplificar el código.
·         Reducción de intensidad
Siempre que sea posible es conveniente sustituir un operador más costoso (multiplicación y división) por otro menos costoso (suma y resta).

EMPAQUETAMIENTO DE TEMPORALES
Se trata de reusar los temporales con el fin de ahorrar espacio. Después de haber optimizado el código es normal pensar que se puedan usar menos temporales.

MEJORA DE LOS LAZOS
  • ·         Traslado de código
Una modificación importante que disminuye la cantidad de código en un lazo es el traslado de código. Esta transformación toma una expresión que produce el mismo resultado independientemente del número de veces que se ejecute un lazo y la coloca antes del lazo.
  • ·         Variables de inducción
Se trata de identificar las variables que permanecen ligadas entre sí en la ejecución, están relacionadas entre sí de forma que conocido el valor de una se puede obtener el de la otra.