13 puntos por GN⁺ 2025-09-30 | 1 comentarios | Compartir por WhatsApp
  • Este artículo explica, con un enfoque basado en casos, un intento y diseño para compilar por adelantado (AOT) código Python puro y convertirlo en ejecutables multiplataforma
  • La idea central es no crear un nuevo JIT ni reescribir todo en C++, sino producir kernels optimizados mediante una tubería de trazado simbólico → IR → generación de código C++ → compilación para múltiples objetivos
  • Usa anotaciones de tipos PEP 484 para iniciar la propagación de tipos, y generación de código con IA para implementar automáticamente cientos de operadores en C++, cubriendo una amplia variedad de llamadas de librerías como Numpy, OpenCV y PyTorch
  • Adopta una estrategia de optimización empírica del rendimiento que genera y despliega en masa distintas rutas de implementación para una misma función de Python, y elige la variante más rápida con telemetría medida en producción
  • El objetivo es ofrecer binarios portables, pequeños y rápidos que no dependan de contenedores, para usarlos como unidad de despliegue que pueda ejecutarse en cualquier lugar, desde servidor y escritorio hasta móvil y web

Foreword

  • La simplicidad y productividad de Python son ventajas, pero existen límites de rendimiento y portabilidad en cargas de trabajo exigentes
  • Este texto del autor invitado Yusuf Olokoba presenta el diseño de un compilador para crear ejecutables rápidos y portables manteniendo el Python original
  • Es un enfoque que busca lograr optimización de kernels construyendo una tubería sin agregar un JIT ni hacer una reescritura total a C++

Introduction

  • La meta es compilar Python sin modificaciones de forma totalmente AOT para que funcione sin intérprete, sea casi tan rápido como C/C++ y pueda ejecutarse en cualquier plataforma
  • A diferencia de intentos previos como Jython, RustPython, Numba, PyTorch o Mojo, elige transformación de código y generación de kernels en vez de reemplazar el lenguaje o el runtime
  • Estas funciones de Python compiladas ya se usan en miles de dispositivos cada mes

Containers Are the Wrong Way to Distribute AI

  • En despliegues reales, los contenedores llevan una carga excesiva —intérprete, paquetes e instantánea del SO—, lo que provoca demoras de arranque y restricciones de portabilidad
  • La alternativa son ejecutables autocontenidos que incluyen solo el modelo, con menor tamaño, arranque más rápido y capacidad de ejecución en servidor, escritorio, móvil y web
  • La idea de fondo es cambiar la unidad de despliegue: dejar de usar una instantánea del SO y pasar a un binario autoejecutable

Arm64, Apple, and Unity: How It All Began

  • Durante la transición de Apple a arm64, se tomó como referencia el caso de Unity, que con IL2CPP convertía CIL a C++ para poder compilar a todos los objetivos
  • La visión fue aplicar la misma idea a Python para asegurar rutas de código que puedan correr en cualquier lugar

Sketching Out a Python Compiler

  • El diseño general se compone de las etapas entrada en Python → traza simbólica (IR) → generación de C++ → compilación multiobjetivo
  • La razón para elegir C++ como producto intermedio en vez de ir directo a código objeto desde el IR es aprovechar al máximo rutas de aceleración como CUDA, MLX, TensorRT y AMX
  • El objetivo es obtener un diseño extensible en el que sea fácil insertar la ruta óptima para cada hardware

Building a Symbolic Tracer for Python

  • El trazado inicial basado en PyTorch FX tenía limitaciones por la necesidad de ejecución y por estar restringido a operaciones de PyTorch
  • En su lugar, se construyó un trazador simbólico basado en análisis de AST que convierte flujo de control y resolución de llamadas a IR
  • El trazador actual ofrece funciones como análisis estático, evaluación parcial y observación de valores en vivo basada en sandbox

Lowering to C++ via Type Propagation

  • Para tender un puente entre el tipado dinámico de Python y el tipado estático de C++, se usa propagación de tipos
  • Si se conocen los tipos de los argumentos de entrada, los tipos de las variables intermedias pueden inferirse de forma determinista según la definición de los operadores
  • Cada operación de Python se mapea a su implementación equivalente en C++, y los tipos se propagan a lo largo de toda la función

Seeding the Type Propagation Process

  • Para iniciar la propagación de tipos, se usan anotaciones de tipos PEP 484
  • Aunque esto entra en tensión con el principio de no modificar el código original, se considera una concesión aceptable por su interfaz concisa y compatibilidad
  • También se imponen restricciones, como limitar la cantidad de tipos en la firma de una función, para garantizar una interfaz de consumo simple

Building a Library of C++ Operators

  • No hace falta implementar todas las funciones directamente en C++; solo las operaciones hoja que no pueden trazarse requieren implementación manual o automática
  • Como gran parte del código Python se compone de una pequeña combinación de operaciones básicas, el conjunto de operadores a cubrir es relativamente pequeño
  • Con generación de código basada en LLM e infraestructura de restricciones, pruebas y compilación condicional, se automatiza la implementación de cientos de funciones de Numpy, OpenCV y PyTorch, entre otras

Performance Optimization via Exhaustive Search

  • Partiendo de la lección de que la optimización del rendimiento es siempre empírica, se generan todas las variantes posibles de implementación y se elige la óptima mediante comparación con mediciones reales
  • Por ejemplo, en Apple Silicon, incluso para resize se generan múltiples rutas como Accelerate, vImage, Core Image y Metal, y se despliegan varios binarios con la misma funcionalidad
  • Con telemetría detallada se recopila la latencia por ruta, y un modelo estadístico predice y selecciona la variante más rápida
  • En la práctica, esto ofrece al usuario una experiencia de ejecución que se vuelve automáticamente más rápida con el tiempo

Designing a User Interface for the Compiler

  • Para que la experiencia de desarrollo tenga una curva de aprendizaje casi nula, se adopta el decorador PEP 318 @compile como interfaz
  • El CLI usa el decorador como punto de entrada para recorrer y compilar el grafo de código dependiente
  • Los argumentos del decorador aceptan tag, description, sandbox y metadata, con soporte para reproducción de entornos y selección de backend como ONNXRuntime, TensorRT, CoreML, IREE y QNN

Closing Thoughts

  • Elementos como excepciones, lambdas, recursión y clases tienen soporte parcial o nulo, y en especial se necesita ampliar la propagación de tipos para tipos compuestos y de orden superior
  • La experiencia de depuración sigue siendo un reto, ya que la compilación optimizada reduce la información simbólica y vuelve más difícil el rastreo
  • std::span, concepts y coroutines de C++20 son bases clave, mientras que std::generator, <stdfloat> y <stacktrace> de C++23 contribuirán a streaming, half/bfloat16 y rastreo de excepciones
  • La meta final es establecer una unidad de despliegue para cargas de trabajo de IA como embeddings y detección mediante ejecutables pequeños, rápidos y seguros, sin contenedores y capaces de ejecutarse en cualquier lugar

1 comentarios

 
secret3056 2025-09-30

Pensé que era algo como APE, pero no lo es.