Qué Hace un Compilador - Historia de los

Anuncio
OpenStax-CNX module: m38391
1
Qué Hace un Compilador - Historia
de los Compiladores
∗
José Enrique Alvarez Estrada
Based on What a Compiler Does - History of Compilers† by
Charles Severance
Kevin Dowd
This work is produced by OpenStax-CNX and licensed under the
‡
Creative Commons Attribution License 3.0
Si usted ha formado parte del mundo del cómputo de alto rendimiento desde sus inicios en la década de
1950, le ha tocado programar en varios lenguajes desde entonces. Durante la década de 1950 e inicios de la de
1960, lo hizo en lenguaje ensamblador. La escasez de memoria y las bajas velocidades de reloj hacían que cada
instrucción fuera preciosa. Con pequeñas memorias, el tamaño de los programas era típicamente pequeño,
así que con el lenguaje ensamblador era suciente. Para nales de la década de 1960, los programadores
comenzaron a escribir más código en un lenguaje de alto nivel como FORTRAN. Usar uno de tales lenguajes
hace que el trabajo que usted realice sea más transportable, conable y fácil de mantener. Dado el incremento
en velocidad y capacidad de las computadoras, el costo de usar un lenguaje de alto nivel fue algo que la
mayoría de los programadores estaban dispuestos a aceptar. En la década de 1970, si un programa gastaba
una cantidad particularmente grande de tiempo en cierta rutina, o la rutina formaba parte del sistema
operativo, o se trataba de una biblioteca de uso común, muy probablemente estuviera escrita en ensamblador.
Durante la última parte de la década de 1970 e inicios de la de 1980, los compiladores optimizadores
continuaron mejorando hasta el punto en que el grueso de los programas de propósito general, excepto
las porciones más críticas, se escribían en lenguajes de alto nivel. En promedio, los compiladores generan
mejor código que la mayoría de los programadores humanos de ensamblador. A menudo ello se debe a
que el compilador puede hacer un mejor uso de algunos recursos de hardware, tales como los registros. En
un procesador con 16 registros, un programador debe adoptar alguna convención respecto a qué registros
usar para cada cosa, con el n de poder seguirle la pista al valor que cada uno almacena. Un compilador
puede usar cada registro como le plazca, porque puede darle un seguimiento preciso al momento en que está
disponible para otro uso.
Sin embargo, durante ese periodo, también evolucionó la arquitectura de las computadoras de alto
rendimiento. Cray Research estaba desarrollando procesadores vectoriales en el extremo superior del espectro computacional. Los compiladores todavía no estaban listos para determinar cuándo debían emplear
esas nuevas instrucciones vectoriales. Los programadores se vieron forzados a escribir lenguaje ensamblador
o crear código FORTRAN muy anado que invocaba a las rutinas vectoriales apropiadas en su código. En
∗ Version
1.2: Sep 2, 2011 9:29 am -0500
† http://cnx.org/content/m33686/1.3/
‡ http://creativecommons.org/licenses/by/3.0/
http://cnx.org/content/m38391/1.2/
OpenStax-CNX module: m38391
2
cierto sentido, los procesadores vectoriales hicieron girar las manecillas del reloj en el sentido inverso, hasta
que comenzaron a conar en el compilador. Los programadores nunca regresaron completamente al lenguaje
ensamblador, pero su código FORTRAN comenzaba a lucir más como no-FORTRAN. Conforme maduraron
las computadoras vectoriales, sus compiladores fueron progresivamente capaces de detectar cuándo podía
realizarse la vectorización. Y en algún momento nuevamente se hicieron mejores que los programadores
humanos en tales arquitecturas. Los nuevos compiladores reducían la necesidad de usar intensivamente
directivas o extensiones del lenguaje.1
La revolución RISC descansaba en una dependencia creciente respecto al compilador. Programar en los
primeros procesadores RISC, tales como el Intel i860 resultaba doloroso si se comparaba con los procesadores
CISC. Sutiles diferencias en la forma de codicar un programa en lenguaje máquina podían tener un impacto
signicativo en el rendimiento global del programa. Por ejemplo, un programador debía contar los ciclos
transcurridos entre una instrucción de carga y el uso de los resultados de la carga en una instrucción posterior
de cálculo. Conforme se desarrollaron procesadores superescalares, se hizo necesario volver simultáneas
ciertos pares de instrucciones, y otras debían acomodarse secuencialmente. Dado que había en el mercado
un gran número de procesadores RISC distintos, los programadores no tenían tiempo de aprender tales
particularidades para lograr hasta la última gota de rendimiento en cada procesador. Era más fácil mantener
unidos al diseñador del procesador y el escritor del compilador (que con suerte trabajaban para la misma
compañía) y hacer que discutieran a fondo la mejor forma de generar el código máquina. Y entonces
cualquiera podía usar el compilador y obtener código que hiciera un uso razonablemente bueno del hardware.
El compilador se convirtió en una herramienta importante en el ciclo de diseño del procesador. Los
diseñadores de procesadores tenían mucha mayor exibilidad respecto a los tipos de cambios que podían
hacer. Por ejemplo, podía suceder que un buen diseño en la siguiente revisión de un procesador ejecutase
código previo 10% más lento que una revisión nueva, pero recompilando el código lo hiciera 65% más rápido.
Por supuesto, era importante proporcionar dicho compilador con cada nuevo embarque de procesadores, y
que dicho compilador diera ese nivel de rendimiento en una amplia variedad de código, y no sólo en una suite
de pruebas de escritorio en particular.
1 Los Ciclos de Livermore eran una prueba comparativa que especícamente comprobaba la capacidad del compilador para
optimizar ecientemente un conjunto de ciclos. Además de ser una prueba comparativa de rendimiento, también comparaba
los compiladores.
http://cnx.org/content/m38391/1.2/
Descargar