¿Son los compiladores la pieza de software más difícil de desarrollar?

No, los compiladores no son el tipo de software más difícil de desarrollar. El tipo de software más difícil de desarrollar es el software en tiempo real, de alto rendimiento y vital para el control de dispositivos físicos.

Cuando era estudiante universitario, era común que los estudiantes de tercer año desarrollaran un compilador Pascal durante un curso de un semestre.

Los compiladores se descomponen en un front-end y back-end.

El front end toma el código fuente, lo tokeniza, analiza los tokens y genera una representación interna del resultado, generalmente un árbol de sintaxis abstracta. Los desafíos en la parte frontal incluyen una salida de error significativa y un análisis inequívoco de la entrada. Para algunos idiomas esto es fácil: la gramática BNF para Pascal se ajusta en un par de páginas. Pascal – Sintaxis en notación BNF Contraste esto con C ++ que es increíblemente difícil de analizar. (Veo esto como una gran debilidad del lenguaje). ¿Existe una gramática estándar de C ++?

El back-end toma el árbol de sintaxis abstracta y lo traduce a código de máquina que se puede ejecutar en la arquitectura de destino. Crear un back-end básico es tedioso pero relativamente simple. Se vuelve difícil cuando comienzas a tratar de optimizar, especialmente con despacho múltiple, ejecución especulativa, múltiples núcleos, coherencia de caché … La optimización correcta de la salida requiere un análisis picayune de la hoja de datos del procesador y mucho análisis.

El software en tiempo real, de alto rendimiento y vital para controlar dispositivos físicos es difícil porque:

  1. En tiempo real significa que debe tener un rendimiento determinista para todas las condiciones. No quiere que el piloto automático de su avión de reacción se comporte de manera irregular bajo la lluvia. Los dispositivos físicos en tiempo real + significan que generalmente no puede usar un punto de interrupción para depurar el programa: ¡esa máquina de escáner CAT continuará bombeando rayos X mientras entra en una función!
  2. Alto rendimiento en este contexto significa que está cerca del límite de lo que puede hacer el procesador; está limitado en el registro que puede generar sin cargar el sistema, de modo que no se puede lograr en tiempo real.
  3. Crítico para la vida significa que las personas pueden morir si el software no funciona. Trabajé en una empresa de Wall Street y, en voz baja, enfatizaron que su tasa de error de software tenía que ser inferior a uno en un millón. Inapropiadamente, me reí: ¡el último lugar donde trabajé fue en sistemas de radiocomunicación de emergencia, donde validamos las tasas de error a menos de uno en mil millones!
  4. Los dispositivos físicos exhiben todo tipo de comportamiento inesperado: stiction, resonancia mecánica, incluidos subarmónicos, desgaste, envejecimiento, temperatura.

Mi madre quería que yo fuera médico. No quería hacer eso porque no quería tener un mal día y matar a alguien. Es curioso, ahora trabajo en software vital donde un mal día podría matar a muchas personas. Y soy muy consciente de eso.

Los compiladores son bastante triviales.

Puede hacerlos con un tokenizador bastante simple y un montón de strcmp (), en su mayor parte. Si desea volverse más complicado y generar código (algo) optimizado, puede usar algunas estructuras de datos simples y realizar transformaciones conmutativas y asociativas en las declaraciones (por ejemplo, usando un árbol cuádruple u otras técnicas).

Algo cerca de la parte superior de la escala “más difícil” sería escribir un controlador DDR4 para vivir en una parte PAL / FPGA, ya que tiene que explotar y debe manejar todo el tiempo, manteniendo la interfaz eléctrica resultante “en Especificaciones.”. Un tiempo tan ajustado es un verdadero error, especialmente si permite que la parte se sincronice hacia arriba y hacia abajo, ya que las variaciones entre los valores de salida del pin no van a escalar linealmente.

Otra persona mencionó los sistemas de soporte vital. Esos son bastante difíciles, porque tienen que ser matemáticamente comprobables, ya que si tienes un error: una o más personas mueren.

Jesus Monroy Jr. (será difícil encontrar una figura más controvertida en los círculos de CS a principios de la década de 1990) escribió un artículo en comp.os.minux en el Día de San Patricio en 1993 titulado HANG THE ENGINEER: agravio de las consecuencias. En él se lamentaba de que los ingenieros de software deberían tener el mismo estándar que los ingenieros civiles en Roma, que tenía dos premisas:

  1. Un buen ingeniero consiguió todo lo que quería, independientemente del costo.
  2. Un mal ingeniero, uno cuyo puente se cayó, quedó colgado

Así que creo que la dificultad surge de tres direcciones:

  • Complejidad (los compiladores no son tan complejos)
  • Repetibilidad (los compiladores tienden a no tener problemas de sincronización)
  • Consecuencias del error (los compiladores tienen pocos [1])

Entonces, no, en general, los compiladores son relativamente fáciles de escribir.


[1] A menos que el compilador se use para compilar un sistema de soporte vital

Durante mi carrera, a veces tenía que escribir analizadores para lenguajes de comando, y solía preguntarme qué tan bien escribiría un compilador de lenguaje de programación, lo que implicaría no solo analizar, sino generar código.

Muchos años después obtuve un trabajo en el que mi experiencia en C y C ++ (me había enseñado C ++ mientras trabajaba desde casa en un trabajo anterior) fue una razón principal para ser contratado. Trabajé durante un año en partes de una máquina virtual para un lenguaje de programación patentado que comenzó a funcionar al mismo tiempo que Python y que era similar a este.

En el curso de ese trabajo tuve que escribir el generador de código, que producía flujos de código de bytes para que la VM los interpretara. Tenía suficiente experiencia como para poder hacer esto bien, y la prueba fue algo satisfactoria, todos los días, al ver millones de líneas de código compiladas por personas que usan el lenguaje, y nunca se generó ningún código incorrecto, lo que suena como una jactancia, pero en realidad no lo es, porque muy pronto descubrirías si había un problema.

Luego pasé a otro trabajo en la misma compañía, escribiendo código en el lenguaje propietario, y después de haber escrito el generador de código, supe cuál era la mejor manera de escribir código eficiente, lo cual fue bastante útil. Solía ​​revisar una gran cantidad de código por parte de juniors, y me encontraba educándolos gentilmente sobre cómo escribir código eficiente mientras conservaba su inteligibilidad.

De todos modos, como ya no estaba trabajando en la propia máquina virtual, y como nunca había escrito un compilador en su totalidad, pero siempre sentí que quería hacerlo en algún momento, lo decidí, en mi tiempo libre en casa, mientras al mismo tiempo criando a mis hijos como madre soltera.

En mayo de 2000, finalmente tuve un compilador funcional, primitivo y que necesitaba refinamiento, después de unos 6 meses o más. Pero al mismo tiempo sabía que mi salud estaba disminuyendo y no sabía por qué. A veces me sentaba y meditaba si estaba acelerando mi desaparición con este trabajo, que era bastante intenso, y que tontamente no les estaba dando a mis hijos el tiempo que se merecían.

Las personas desarrollan muchas cosas buenas y nunca se convierten en productos comerciales. Tampoco el mío. No me importa Hace lo que quiero. De hecho lo uso mucho. He escrito una serie de aplicaciones con él.

En mi implementación, utilicé un enfoque que puede ser conocido, tal vez no, pero me pareció una buena idea, y no me importó lo que nadie más había hecho: omití la conversión al código de bytes y permití cada análisis nodo para ser una clase C ++ con una función de ejecución. Entonces, cuando ejecuta algo, efectivamente está llamando a esa función en un nodo en la parte superior de un árbol de nodos de análisis, y cada nodo evalúa sus elementos secundarios, y así sucesivamente.

Esto funciona, aunque más lentamente que una implementación convencional. Significaba que todo el código fuente podía compilarse a pedido, lo que resultó ser un poco lento, por lo que arreglé almacenar en caché los árboles de análisis generados serializándolos en archivos. Así que tuve que escribir código para serializar y deserializar los árboles de análisis.

Entonces me di cuenta de que podía usar el mismo truco para almacenar datos, es decir, tomar una estructura de datos compleja en memoria y simplemente serializarla en un archivo. Esto ahorra el horror de inventar formatos de archivos binarios. Los datos vuelven a aparecer tal como estaban cuando salieron. Esto hace las cosas mucho más fáciles.

Estas son solo algunas de las ideas que incorporé. También hay una GUI que hace el diseño automático de los controles en los cuadros de diálogo, un paquete matemático que maneja las matemáticas enteras con cualquier nivel de precisión, y así sucesivamente. No por ningún objetivo comercial, sino porque tenía ganas de hacerlo.

En general, puedo decir que no fue fácil hacer lo que hice, pero fue factible, y he abordado cosas más difíciles en mi trabajo diario. Pero cuando se trata de puro placer, escribir sus propias cosas sin todas las restricciones comerciales es maravilloso. Y supongo que me ayudó a hacer frente a las limitaciones de mi trabajo diario, porque tenía una manera de desahogarme y hacer todas las cosas (incluso cometer muchos errores y aprender de ellos) que no se me permitía hacer en trabajo.

Entonces no, escribir un compilador no es difícil. Una vez que haya decidido el formato del idioma de origen, y planificado un tokenizador, y haya decidido qué tipos de nodos de árbol de análisis desea tener, el resto es solo cuestión de conectarlo hasta que esté listo. Luego puede pensar en la guinda del pastel, como optimizar el código, mejorar el lenguaje, escribir bibliotecas de soporte en tiempo de ejecución, etc. Un paso a la vez.

Pero emitiré una advertencia que no debe subestimar la importancia de: No se esfuerce por luchar contra su propia salud. Aprenda a moderar su entusiasmo y acepte que trabajar en algo agradable como este es un lujo que pocos pueden tener, y que no puede hacerlo usted mismo, excluyendo todo lo que sucede en su vida, particularmente sus relaciones con su propia carne y hueso.

Un par de años después de que mi compilador comenzó a funcionar, me diagnosticaron una enfermedad autoinmune rara y casi muero. Esta fue una lección saludable en la vida. Eventualmente me llevó a mi jubilación anticipada. Sujeto a mi salud, ahora puedo escribir el código que quiera, pero he logrado la mayoría de mis ambiciones de programación, y mi enfoque en estos días es disfrutar de la vida familiar, sin saber (¡como es el caso de todos!) Cuánto hay izquierda.

Entonces, ¿qué sería más difícil que escribir un compilador? Bueno, trabajando en un proyecto con decenas de millones de líneas de código escritas por una diversidad de personas durante varias décadas, y adquiriendo suficiente experiencia para poder saltar a cualquier parte de ese código, tener una idea de lo que hace y cómo funciona. lo hace y realiza cambios en formas arquitectónicamente sólidas, sin desestabilizarlo o introducir restricciones futuras involuntarias (es decir, deuda técnica). Y contra plazos, y con restricciones presupuestarias, y con multas por retraso en la entrega. Estuve allí, obtuve la colección de camisetas, luché para mantener la cordura y la salud mientras estaba bajo presión, y felizmente lograron retirarse sin arrepentirse.

¡Diablos no! Pude escribir un compilador mientras tomaba un curso en línea gratuito de un semestre. Es una actividad común en un curso de informática de pregrado de nivel junior.

Dicho esto, escribir un compilador de vanguardia con optimizaciones complejas que exprimen al máximo el hardware moderno es significativamente más complejo que escribir un compilador simple de proyectos de un semestre. Hay personas que hacen carreras de tales cosas.

Todavía es un área de investigación, y queda mucho por aprender. Aún así, no es el software más difícil de desarrollar.

El software más difícil que se me ocurre para desarrollar correctamente es cualquier cosa que ponga vidas en peligro. Ya veo, mirando las otras respuestas, no estoy solo en esta creencia. Esto es por muy buenas razones.

Debe asegurarse de que el software sea a prueba de balas. Si hay plazos en tiempo real (y si hay vidas involucradas, ciertamente las hay), deben cumplirse. El hardware puede fallar y fallará, y el software debe hacer todo lo posible para minimizar o mitigar el impacto y garantizar un modo de falla adecuado y seguro.

Hay un montón de otros puntos aquí. Parece que las otras personas que respondieron antes que yo las han cubierto bastante bien.

Para una historia de advertencia, lea sobre Therac-25.

No y sobre todo no.

Un compilador básico es realmente bastante fácil. Incluso cuando estaba en la universidad a principios de los años 80, el análisis y la generación de códigos eran bien entendidos. Fue tan fácil que todos escribieron un compilador para un lenguaje razonable en un solo trimestre.

El siguiente paso es generar un código más eficiente u optimizar ciertos patrones de codificación. No hay problema, agregue un análisis de patrones y haga que la generación de código sea más compleja. Repite tantas veces como quieras porque esa madriguera de conejo es infinitamente profunda.

Básicamente, puede hacer que compilar un compilador sea un proyecto muy complejo, pero no creo que sea particularmente difícil en comparación con muchas otras piezas de software. Ciertamente hay muchas cosas interesantes que se pueden optimizar. Particularmente encuentro que las optimizaciones del compilador JIT (Just In Time) son particularmente fascinantes. Pero aún más interesante y complejo que difícil.

No, definitivamente no.

Supongo que lo que es más difícil es conseguir un compilador para generar el código de máquina más optimizado, el problema es muchas veces que esto se considera parte del proceso básico de desarrollo del analizador (que es bastante fácil hoy en día). La mayoría de los buenos compiladores todavía están en desarrollo, incluso hoy en día hay varios desarrolladores de compiladores que constantemente agregan nuevas optimizaciones al código generado e intentan extraer más del hardware subyacente.

Sin embargo, eso (generar el código de máquina más optimizado) es un problema bastante difícil, que todavía está bajo el enfoque de investigación a nivel global.

Pero sí, el software de súper precisión definitivamente puede considerarse como uno de los más difíciles de desarrollar e implementar entre todos.

Hubo un tiempo en que sí, pero en estos días hay más herramientas para decirte que no te has equivocado. El problema es más la parte de optimización que el compilador básico, y las herramientas como los simuladores simbólicos pueden decirle cuándo está cometiendo errores.

Los sistemas de procesamiento en paralelo son generalmente difíciles porque los humanos son malos para pensar en paralelo. De los proyectos que sé, este es probablemente el más complicado:

Xyce

Si desea considerar el diseño de hardware / IC como un tipo de software (escribir código HDL), lo consideraría muy difícil, junto con escribir código de banco de pruebas para verificar que los sistemas funcionarán.

Los compiladores de hoy son maravillas del arte de la ingeniería de software. Los mejores parecen tímidos de poder escribir software por su cuenta.

Pero no son el software más difícil de desarrollar.

Probar un compilador es bastante sencillo, la salida del compilador debe cumplir exactamente con una especificación detallada.

Es mucho más difícil escribir software criptológico. Escribir software criptográfico requiere conocimiento matemático especializado, es mucho más complejo que cualquier compilador.

Y realmente, no importa cuántas pruebas hagas, nunca puedes estar seguro de que no hay un error en el código en alguna parte. Y sabe que si su software se usa para algún propósito serio, algunos de los mejores ingenieros de software de cifrado del mundo, incluidos los actores de los estados nacionales, intentarán encontrar ese error.

De ningún modo.

Los compiladores pueden ser complicados, sin duda, pero hay una colección tan grande de teoría y práctica de la informática que los rodea, que se apoya en los hombros de los gigantes. Sistemas como The LLVM Compiler Infrastructure Project hacen posible que personas con habilidades razonables creen compiladores portátiles incluso en lenguajes bastante complejos.

Sistemas que están mal definidos o entendidos, o que interactúan con dichos sistemas, o tienen restricciones de tiempo real, o deben ser escalables, o …

No hay una clase real de aplicaciones que sean “más difíciles”.

En algún momento de la historia podrían considerarse difíciles, pero un compilador es “solo” un traductor de un idioma a otro. Actualmente, la mayoría de los problemas de ingeniería complicados implican el tiempo como parámetro. Piense en sistemas distribuidos masivamente o sistemas de software que involucren millones de parámetros como redes neuronales profundas (que entre todos esos parámetros tienen tiempo involucrado).

Supongo que sería bueno definir “duro” en este contexto. Por lo tanto, suponiendo que “difícil” significa algoritmos complejos, entonces los compiladores son muy difíciles de desarrollar principalmente debido a las optimizaciones involucradas en los compiladores de última generación. Pero no diría que los compiladores son los “más difíciles” en este sentido, porque hay muchas áreas que son igual de difíciles. Algunos de los ejemplos con los que estoy familiarizado: herramientas de automatización de diseño electrónico (EDA) (verificación formal, síntesis, lugar y ruta, tiempo, ATPG, etc.), IA (aprendizaje automático, razonamiento simbólico). Estoy seguro de que hay otros.

A2A:

No. La teoría detrás de escribir compiladores es un área muy bien entendida. Algunos aspectos no son fáciles de asimilar sin algún estudio. Los compiladores pueden ser grandes y complejos. Pero no son las piezas de software más difíciles de desarrollar.

El compilador es bastante fácil de escribir. Esto es esencialmente un análisis de la sintaxis seguida de la generación de código.

Cuanto más aparece la “Forma libre” en un lenguaje, más complejo se vuelve el “análisis de sintaxis”. Esa es la razón por la cual algunos lenguajes como C ++ tienen una pobre estandarización de la interpretación.

La parte difícil de un “lenguaje informático verdaderamente general” es generalmente el diseño en sí. La sintaxis y la amabilidad con el analizador elegido, la función, el tipo de datos, la estructura de datos, la relación con los datos y la función, OO o estándar, recolección de basura, concepto de subprocesos, etc.

Fue hace 50 años, pero hoy con LEX, YACC y gramáticas (BNF, LR (K)) lo hacen mucho más fácil. Tienes que escribir muchas pequeñas rutinas que generan las acciones en los trozos, pero no creo que sea difícil.

Las herramientas de hoy crean el marco de un compilador para un lenguaje particular definido por la gramática.

Un compilador de calidad de producción es un esfuerzo de equipo. Generar un buen código es bastante difícil. Un compilador básico que usa un generador de código existente no es tan difícil.

Hay muchas cosas difíciles de codificar. Los compiladores son solo uno de ellos.