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/