martes, 22 de octubre de 2019
lunes, 21 de octubre de 2019
4.4 Administrador de memoria
La administración de la memoria es un proceso hoy en día muy importante, de tal modo que su mal o buen uso tiene una acción directa sobre el desempeño de memoria. En general un ensamblador tiene un administrador de memoria más limitado que un compilador; en la mayoría de los lenguajes de programación el uso de punteros no estaba vigilado por lo que se tienen muchos problemas con el uso de memoria. Los lenguajes más recientes controlan el uso de punteros y tienen un programa denominado recolector de basura que se encarga de limpiar la memoria no utilizada mejorando el desempeño.
La memoria principal puede ser considerada como un arreglo lineal de localidades de almacenamiento de un byte de tamaño. Cada localidad de almacenamiento tiene asignada una dirección que la identifica
Se distinguen los siguientes propósitos del sistema de administración de memoria:
Protección.
Si varios programas comparten la memoria principal, se debería asegurar que el programa no sea capaz de cambiar las ubicaciones no pertenecientica él. Aunque una acción de escritura puede tener efectos más graves que una de lectura, esta última tampoco debería estar permitida, para proporcionar algo de privacidad al programa.
Compartimiento.
Este objetivo parece contradecir al anterior, sin embargo a veces es necesario para los usuarios poder compartir y actualizar información (por ejemplo, en una base de datos) y, si se organiza la tarea de entrada a la misma, se puede evitar el tener varias copias de la rutina.
Reubicación.
La técnica de multiprogramación requiere que varios programas ocupen la memoria al mismo tiempo. Sin embargo no se sabe con anticipación donde será cargado cada programa por lo que no es práctico usar direccionamiento absoluto de memoria.
Organización física.
Debido al costo de una memoria principal rápida, éste se usa en conjunto con una memoria secundaria mucho más lenta (y por consiguiente, barata) a fines de extender su capacidad.
Organización lógica.
Aunque la mayor parte de las memorias son organizadas linealmente con un direccionamiento secuencial, esto difícilmente concuerde con el camino seguido por el programa, debido al uso de procedimientos, funciones, subrutinas, arreglos, etc
4.3 Lenguaje Máquina
Es el que proporciona poca o ninguna abstracción del microprocesador de un ordenador. El lenguaje máquina solo es entendible por las computadoras. Se basa en una lógica binaria de 0 y 1, generalmente implementada por mecanismos eléctricos. En general el lenguaje maquina es difícil de entender para los humanos por este motivo hacemos uso de lenguajes más parecidos a los lenguajes naturales.
Se denomina lenguaje máquina a la serie de datos que la parte física de la computadora o hardware, es capaz de interpretar. El lenguaje máquina fue el primero que empleo el hombre para la programación de las primeras computadoras. Una instrucción en lenguaje máquina puede representarse de la siguiente forma: 011011001010010011110110. Esta secuencia es fácilmente ejecutada por la computadora, pero es de difícil interpretación, siendo aún más difícil la interpretación de un programa (conjunto de instrucciones) escrito de esta forma.
Esta dificultad hace que los errores sean frecuentes y la corrección de los mismos costosa, cuando no imposible, al igual que la verificación y modificación de los programas.
Características:
El lenguaje máquina realiza un conjunto de operaciones predeterminadas llamadas micro operaciones. Las micro operaciones sólo realizan operaciones del tipo aritmética (+,- ,*,/), lógicas (AND, OR, NOT) y de control (secuencial, de control y repetitiva). El lenguaje maquina es dependiente del tipo de arquitectura. Así un programa máquina para una arquitectura Intel x86 no sé ejecutara en una arquitectura Power PC de IBM (al menos de manera nativa).
Algunos microprocesadores implementan mas funcionalidades llamado CISC, pero son más lentos que los RISC ya que estos tienen registros más grandes.
Ventajas
· Mayor adaptación al equipo.
· Máxima velocidad con mínimo uso de memoria.
Desventajas
· Imposibilidad de escribir código independiente de la máquina.
· Mayor dificultad en la programación y en la comprensión de los programas.
· El programador debe conocer más de un centenar de instrucciones.
· Es necesario conocer en detalle la arquitectura de la máquina.
4.2 Lenguaje ensamblador
¿Qué es?
El lenguaje Assembly (Urbina, 2011) (a veces mal llamado "Ensamblador" por su traducción literal al español) es un tipo de lenguaje de bajo nivel utilizado para escribir programas informáticos, y constituye la representación más directa del código máquina específico para cada arquitectura de computadora
Segunda generación de lenguajes
Versión simbólica de los lenguajes máquina (Urbina, 2011) (MOV, ADD).La comunicación en lenguaje de máquina es particular de cada procesador que se usa, y programar en este lenguaje es muy difícil y tedioso, por lo que se empezó a buscar mejores medios de comunicación con ésta. Los lenguajes ensambladores tienen ventajas sobre los lenguajes de máquina.
Este lenguaje fue usado ampliamente en el pasado para el desarrollo de software, pero actualmente sólo se utiliza encontradas ocasiones, especialmente cuando se requiere la manipulación directa del hardware o se pretenden rendimientos inusuales de los equipos
Características:
El programa lee un archivo escrito en lenguaje ensamblador y sustituye cada uno de los códigos mnemotécnicos por su equivalente código máquina. Los programas se hacen fácilmente portables de máquina a máquina y el cálculo de bifurcaciones se hace de manera fácil.
Clasificación:
· Ensambladores básicos: Son de muy bajo nivel, y su tarea consiste básicamente, en ofrecer nombres simbólicos a las distintas instrucciones, parámetros y cosas tales como los modos de direccionamiento
· Ensambladores modulares, o macro ensambladores: Descendientes de los ensambladores básicos, fueron muy populares en las décadas de los 50 y los 60, fueron antes de la generalización de los lenguajes de alto nivel. Un macroinstrucción es el equivalente a una función en un lenguaje de alto nivel.
Operaciones básicas
(Urbina, 2011) Las operaciones básicas en un lenguaje ensamblador son la suma la resta la multiplicación y la división y Necesitara un poco más de información sobre la arquitectura y SO para el cual programas.
Pero la idea básica es:
--definir que parámetros tendrá la función.
--hacer el programa, propiamente dicho, en assembler.
Siguiendo la convención de pasaje de parámetros, manejará registros y posiciones de memoria, devolviendo los resultados en donde deba (una posición de memoria, el registro eax, etc.).
4.1 Registros
¿Qué son?
Los registros son la memoria principal de la computadora. Existen diversos registros de propósito general y otros de uso exclusivo. Algunos registros de propósito general son utilizados para cierto tipo de funciones. Existen registros acumuladores, puntero de instrucción, de pila, etc.
Los registros son espacios físicos dentro del microprocesador con capacidad de 4 bits hasta 64 bits dependiendo del microprocesador que se emplee.
¿Quiénes lo utilizan?
Antes de nada, para el desarrollo de esta parte hablaremos indistintamente de registros de activación o de marcos de pila. Esto se debe a que en la documentación encontrada sobre el manejo de los registros ebp y esp se hace mención a dicho concepto de marco de pila. Puesto que el lenguaje permite recursividad, los registros de activación se asignan dinámica mente.
Distribución
La UCP o CPU tiene 14 registros internos, cada uno de ellos de 16 bits (una palabra). Los bits están enumerados de derecha a izquierda, de tal modo que el bit menos significativo es el bit 0.
Los registros se pueden clasificar de la siguiente forma:
Registros de datos:
AX: Registro acumulador. Es el principal empleado en las operaciones aritméticas.
BX: Registro base. Se usa para indicar un desplazamiento.
CX: Registro contador. Se usa como contador en los bucles.
DX: Registro de datos.
Estos registros son de uso general y también pueden ser utilizados como registros de 8 bits, para utilizarlos como tales es necesario referirse a ellos como por ejemplo: AH y AL, que son los bytes alto (high) y bajo (low) del registro AX. Esta nomenclatura es aplicable también a los registros BX, CX y DX.
Registros de segmentos:
CS: Registro de segmento de código. Contiene la dirección de las instrucciones del programa.
DS: Registro segmento de datos. Contiene la dirección del área de memoria donde se encuentran los datos del programa.
SS: Registro segmento de pila. Contiene la dirección del segmento de pila. La pila es un espacio de memoria temporal que se usa para almacenar valores de 16 bits (palabras).
ES: Registro segmento extra. Contiene la dirección del segmento extra. Se trata de un segmento de datos adicional que se utiliza para superar la limitación de los 64Kb del segmento de datos y para hacer transferencias de datos entre segmentos.
Registros punteros de pila:
SP: Puntero de la pila. Contiene la dirección relativa al segmento de la pila.
BP: Puntero base. Se utiliza para fijar el puntero de pila y así poder acceder a los elementos de la pila.
Registros índices:
SI: Índice fuente.
DI: Índice destino.
¿Cuales su aplicación en la generación de códigos?
1. usar el registro de y si está en un registro que no tiene otra variable, y además y no
está viva ni tiene uso posterior. Si no:
2. usar un registro vacío si hay. Si no:
3. usar un registro ocupado si op requiere que x esté en un registro o si x tiene uso
Posterior. Actualizar el descriptor de registro. Si no:
4. usar la posición de memoria de x
4 Generación de código objeto
4 Generación de código objeto
El generador de código objeto como lo menciona (Urbina, 2011) transforma el código Intermedio optimizado en código objeto de bajo nivel. Toma código intermedio y genera Código objeto para la máquina considerada Es la parte más próxima a la arquitectura de la Máquina. Habitualmente, se escriben ``a mano´´ desarrollo a medida´ para cada máquina Específica.
3.1.3 Globales
La optimización global se da con respecto a todo el código. Este tipo de
optimización es más lenta pero mejora el desempeño general de todo programa.
Las optimizaciones globales pueden depender de la arquitectura de la máquina.
En algunos casos es mejor mantener variables globales para agilizar los procesos
(el proceso de declarar variables y eliminarlas toma su tiempo) pero consume más
memoria. Algunas optimizaciones incluyen utilizar como variables registros del
CPU, utilizar instrucciones en ensamblador.
3.2.3 Herramientas para el análisis del flujo de datos
Existen algunas herramientas que permiten el análisis de los flujos de datos, entre
ellas tenemos los depuradores y desambladores. La optimización al igual que la
programación es un arte y no se ha podido sistematizar del todo.
Depuradores
Un depurador es una aplicación que permite correr otros programas, permitiendo al usuario ejercer cierto control sobre los mismos a medida que los estos se ejecutan, y examinar el estado del sistema (variables, registros, banderas, etc.) en el momento en que se presente algún problema.
El propósito final de un depurador consiste en permitir al usuario observar y comprender lo que ocurre "dentro" de un programa mientras el mismo es ejecutado.
Desambladores
Es un programa de computador que traduce el lenguaje de máquina a lenguaje ensamblador, la operación inversa de la que hace el ensamblador. la salida de un desensamblador, es a menudo formateada para la legibilidad humana en vez de ser adecuada para la entrada a un ensamblador, haciendo que éste sea principalmente una herramienta de ingeniería inversa.
3.2.2 Criterios para mejorar el código
La mejor manera de optimizar el código es hacer ver a los programadores que
optimicen su código desde el inicio, el problema radica en que el costo podría ser
muy grande ya que tendría que codificar más y/o hacer su código más legible. Los
criterios de optimización siempre están definidos por el compilador.
Muchos de estos criterios pueden modificarse con directivas del compilador desde
el código o de manera externa. Este proceso lo realizan algunas herramientas del
sistema como los ofuscadores para código móvil y código para dispositivos
móviles.
3.2.1 Costo de ejecución (Memoria, registros, pilas)
Los costos de ejecución son aquellos que vienen implícitos al ejecutar el
programa.
En algunos programas se tiene un mínimo para ejecutar el programa, por lo que el
espacio y la velocidad de los microprocesadores son elementos que se deben
optimizar para tener un mercado potencial más amplio.
Las aplicaciones multimedia como los videojuegos tienen un costo de ejecución
alto por lo cual la optimización de su desempeño es crítico, la gran mayoría de las
veces requieren de procesadores rápidos (e.g. tarjetas de video) o de mucha
memoria. Otro tipo de aplicaciones que deben optimizarse son las aplicaciones
para dispositivos móviles.
Los dispositivos móviles tienen recursos más limitados que un dispositivo de
cómputo convencional razón por la cual, el mejor uso de memoria y otros recursos
de hardware tiene mayor rendimiento. En algunos casos es preferible tener la
lógica del negocio más fuerte en otros dispositivos y hacer uso de arquitecturas
descentralizadas como cliente/servidor o P2P.
3.2 Costos
Los costos son el factor más importante a tomar en cuenta a la hora de optimizar
ya que en ocasiones la mejora obtenida puede verse no reflejada en el programa
final pero si ser perjudicial para el equipo de desarrollo. La optimización de una
pequeña mejora tal vez tenga una pequeña ganancia en tiempo o en espacio pero
sale muy costosa en tiempo en generarla.
Pero en cambio si esa optimización se hace por ejemplo en un ciclo, la mejora
obtenida puede ser N veces mayor por lo cual el costo se minimiza y es benéfico
la mejora.
Por ejemplo:
for(int i=0; i < 10000; i++); si la ganancia es de 30 ms 300s
3.1.4 MIRILLA
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
3.1.2 CICLOS
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.1.1 Locales
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
3.1Optimizacion
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).
miércoles, 2 de octubre de 2019
lunes, 30 de septiembre de 2019
2.3.6 Estructuras
2.3.6 Estructuras
Estructura y fases de un compilador (2) Análisis lineal También conocido como: análisis léxico o exploración. Ejemplo, en la proposición de asignación: posicion = inicial + velocidad * 60 Se identifican los siguientes componentes léxicos Identificador (posicion) Símbolo de asignación (=) Identificador (inicial) Signo de suma (+) Identificador (velocidad) Signo de multiplicación (*) Número (60)
12. Estructura y fases de un compilador (3) Análisis jerárquico También llamado análisis sintáctico. Implica agrupar los componentes léxicos en frases gramaticales que el compilador utiliza para sintetizar la salida. Por lo general, las frases gramaticales se representan mediante un árbol de análisis sintáctico. Ejemplo: Proposición de asignación Identificador posición = expresión expresión identificador + expresión inicial expresión identificador * expresión velocidad Número 60
13. Estructura y fases de un compilador (4) La estructura jerárquica de un programa normalmente se expresa utilizando reglas recursivas. Para el ejemplo anterior de la proposición de asignación se tiene: Cualquier identificador es una expresión Cualquier número es una expresión Si expresión1 y expresión2 son expresiones, entonces también lo son: expresión1 + expresión2 expresión1 * expresión2 (expresión1) Proposición de asignación Identificador posicion = expresión expresión identificador + expresión inicial expresión identificador * expresión velocidad Número 60
14. Estructura y fases de un compilador (5) Muchos lenguajes definen recursivamente las proposiciones mediante reglas como: Si identificador1 es un identificador y expresión2 es un identificador, entonces: Identificador1 = expresión2 Si expresión1 es una expresión y proposición2 es una proposición, entonces: while ( expresión1 ) do proposición2 if ( expresión1 ) then proposición2 El análisis lineal (léxico) no es suficientemente poderoso para analizar proposiciones o expresiones recursivas. Cuándo una construcción del lenguaje fuente es recursiva, entonces es factible emplear una gramática libre de contexto para formalizar la recursión.
15. Estructura y fases de un compilador (6) Análisis semántico Revisa el programa e intenta encontrar errores semánticos. Reúne la información sobre los tipos para la fase posterior de generación de código. Un componente importante es la verificación de tipos. Se verifica si cada operador tiene los operandos permitidos. Un real no debe utilizarse como índice de un arreglo. Convertir un número entero a real para algunos operadores. = posicion + inicial * velocidad 60 = posicion + inicial * velocidad ent a real 60 El análisis semántico inserta una conversión de entero a real en el árbol de análisis sintáctico
16. Estructura y fases de un compilador (7) Conceptualmente un compilador opera en fases, cada una de las cuales transforma al programa fuente de una representación a otra. Analizador léxico Programa fuente Analizador sintáctico Analizador semántico Generador de código intermedio Optimizador de código Generador de código Programa objeto Manejador de errores Administrador de la Tabla de símbolos
17. Estructura y fases de un compilador (8) Administración de la tabla de símbolos Registra los identificadores e información referente a ellos. Se tiene un registro por cada identificador. Todas las fases hacen uso de esta tabla. Detección e información de errores En cada fase se pueden encontrar errores. Se debe definir como se deben tratar los errores en cada una de las fases. Las fases de análisis Cambian la representación interna del programa fuente conforme avanza cada una de ellas. Generación de código intermedio Se puede considerar como código para una máquina abstracta. Dicha representación debe ser fácil de producir y fácil de traducir al código objeto. Optimización de código Trata de mejorar el código intermedio de modo que resulte un código máquina más rápido de ejecutar. Generación de código Por lo general se trata de código máquina relocalizable o código ensamblador. Se deben seleccionar posiciones de memoria para cada una de las variables. posicion = inicial + velocidad * 60 Analizador léxico id1 = id2 + id3 * 60 Analizador sintáctico = id1 + id2 * id3 60 Analizador semántico = id1 + id2 * id3 ent a real 60 Generador de código intermedio temp1 = entreal(60) temp2 = id3 * temp1 temp3 = id2 +temp2 Id1 = temp3 Optimizador de código temp1 = id3 * 60.0 temp2 = id2 +temp1 Id1 = temp2 Generador de código MOVF id3, R2 MULF #60.0, R2 MOVF id2, R1 ADDF R2, R1 MOV R1, id1 TABLA DE SIMBOLOS posicion … inicial … velocidad … 1 2 3 4
18. Estructura y fases de un compilador (9) Con frecuencia las fases de un compilador se agrupan en una etapa inicial y una etapa final: Etapa inicial Comprende aquellas fases que dependen principalmente del código fuente. Normalmente incluye el análisis léxico, sintáctico y semántico, la creación de la tabla de símbolos, la generación de código intermedio y cierta optimización de éste. También incluye el manejo de errores correspondientes a cada etapa. Etapa final Comprende aquellas partes del compilador que dependen de la máquina objeto. En general estas partes dependen del lenguaje intermedio, más que del lenguaje fuente. Comprende aspectos de optimización y generación de código, junto con el manejo de errores necesario y las operaciones con la tabla de símbolos. CLR Architecture.PNG
19. Estructura y fases de un compilador (10) Pasadas Consiste en leer un archivo de entrada y escribir uno de salida. Es común que se apliquen varias fases de la compilación en una sola pasada Reducción de pasadas Es deseable tener pocas pasadas dado que la lectura y la escritura de archivos intermedios lleva tiempo. Sin embargo, en ocasiones resulta muy difícil generar código si no se tiene una representación intermedia completa. Por ejemplo: Las instrucciones de tipo goto que saltan hacia delante. En este caso es posible dejar un espacio en blanco y rellenar cuando la información esté disponible
Suscribirse a:
Entradas (Atom)