Simplificaste demasiado la declaración de Guido al formular tu pregunta. El problema no es escribir un compilador para un lenguaje de tipo dinámico. El problema es escribir uno que sea (criterio 1) siempre correcto, (criterio 2) mantiene la escritura dinámica y (criterio 3) es notablemente más rápido para una cantidad significativa de código.
Es fácil implementar el 90% (error de criterio 1) de Python y ser consistentemente rápido en ello. Del mismo modo, es fácil crear una variante de Python más rápida con tipeo estático (criterios de error 2). Implementar 100% también es fácil (en la medida en que implementar un lenguaje tan complejo es fácil), pero hasta ahora todas las formas fáciles de implementar resultan relativamente lentas (criterios de falla 3).
Implementar un intérprete más JIT que sea correcto, implemente todo el lenguaje y sea más rápido para algunos códigos resulta factible, aunque significativamente más difícil (cf. PyPy) y solo si automatizas la creación del compilador JIT (Psyco lo hizo sin él) , pero estaba muy limitado en qué código podría acelerar). Pero tenga en cuenta que esto está explícitamente fuera de alcance, ya que estamos hablando de estática(también conocido como compiladores de antemano). Solo menciono esto para explicar por qué su enfoque no funciona para compiladores estáticos (o al menos no hay un contraejemplo existente): primero tiene que interpretar y observar el programa, luego generar código para una iteración específica de un bucle (u otro código lineal ruta), luego optimice el infierno basado en suposiciones solo verdaderas para esa iteración específica (o al menos, no para todas las iteraciones posibles). La expectativa es que muchas ejecuciones posteriores de ese código también coincidan con la expectativa y, por lo tanto, se beneficien de las optimizaciones. Se agregan algunos controles (relativamente baratos) para asegurar la corrección. Para hacer todo esto, necesita una idea de en qué especializarse, y una implementación lenta pero general a la que recurrir. Los compiladores de AOT no tienen ninguno. No pueden especializarse en absolutobasado en el código que no pueden ver (por ejemplo, código cargado dinámicamente), y especializarse descuidadamente significa generar más código, lo que tiene una serie de problemas (utilización de icache, tamaño binario, tiempo de compilación, ramas adicionales).
Implementar un compilador AOT que implemente correctamente todo el lenguaje también es relativamente fácil: generar código que llame al tiempo de ejecución para hacer lo que haría el intérprete cuando se alimenta con este código. Nuitka (en su mayoría) hace esto. Sin embargo, esto no produce muchos beneficios de rendimiento (criterios de error 3), ya que aún tiene que hacer tanto trabajo innecesario como un intérprete, salvo por enviar el código de bytes al bloque de código C que hace lo que compiló. Pero eso es solo un costo bastante pequeño, lo suficientemente significativo como para que valga la pena optimizarlo en un intérprete existente, pero no lo suficientemente significativo como para justificar una implementación completamente nueva con sus propios problemas.
¿Qué se necesitaría para cumplir con los tres criterios? No tenemos idea Existen algunos esquemas de análisis estático que pueden extraer información sobre tipos concretos, flujo de control, etc. de los programas de Python. Los que producen datos precisos más allá del alcance de un solo bloque básico son extremadamente lentos y necesitan ver todo el programa, o al menos la mayor parte. Aún así, no puede hacer mucho con esa información, aparte de quizás optimizar algunas operaciones en tipos incorporados.
¿Porque eso? Para decirlo sin rodeos, un compilador elimina la capacidad de ejecutar el código Python cargado en tiempo de ejecución (criterio de error 1), o no hace ninguna suposición que pueda ser invalidada por ningún código Python. Desafortunadamente, eso incluye casi todo lo útil para optimizar programas: los globales que incluyen funciones pueden ser rebotados, las clases pueden ser mutadas o reemplazadas por completo, los módulos también pueden modificarse arbitrariamente, la importación puede ser secuestrada de varias maneras, etc. Una sola cadena pasada a eval, exec, __import__o muchas otras funciones, pueden hacer algo de eso. En efecto, eso significa que casi no se pueden aplicar grandes optimizaciones, produciendo pocos beneficios de rendimiento (criterios de error 3). De vuelta al párrafo anterior.