Compilando código JIT en 5μs

La compilación JIT para bases de datos se está reconsiderando con enfoques ultrarrápidos de “copy-and-patch” que pueden generar código máquina en microsegundos, evitando la gran latencia de LLVM y aun así logrando mejoras notables sobre la interpretación. Los comentaristas sopesan estas mejoras de rendimiento frente a las preocupaciones de seguridad al relajar políticas estrictas de W^X, la complejidad y la superficie de ataque de los JIT (especialmente para entradas no confiables) y los ámbitos limitados en los que un JIT compensa el coste añadido de portabilidad y mantenimiento. Varios señalan marcos JIT más ligeros, técnicas de JIT manual en lenguajes como Common Lisp e incluso la generación de código asistida por LLM como formas de hacer más prácticos los JIT especializados.

Seguridad de JIT y W^X

  • Una parte sostiene que JIT es intrínsecamente menos seguro porque requiere memoria escribible y ejecutable, socavando políticas estrictas de W^X y habilitando exploits más potentes.
  • Otros responden que los JIT modernos normalmente asignan páginas RW, escriben el código y luego las cambian a RX, de modo que W^X se preserva por mapeo.
  • Hay debate sobre si esto tiene “implicaciones a nivel de sistema” o si es solo una decisión de endurecimiento por proceso; algunos dicen que el sistema operativo ya debe gestionar esos permisos, otros subrayan que permitir ejecución de código dinámico aumenta el impacto de los errores.
  • Se citan ejemplos de plataformas que restringen fuertemente JIT (iOS, GrapheneOS) y de técnicas como la verificación de bytecode y el etiquetado de memoria por hardware para hacer JIT más seguro.
  • Varios comentarios señalan que los JIT aumentan la superficie de ataque del proceso anfitrión (especialmente al ejecutar entradas no confiables, como navegadores o consultas a bases de datos), pero no otorgan nuevas capacidades más allá de las que el proceso ya tiene.
  • Se invocan ROP y técnicas similares para argumentar que, si un proceso puede ejecutar cualquier código, la ejecución arbitraria de código es efectivamente posible con o sin JIT.

Diseño de JIT, rendimiento y LLVM frente a enfoques ligeros

  • Varios comentarios enfatizan que los JIT basados en LLVM tienen alta latencia y pueden ser inapropiados para cargas de trabajo que necesitan compilación muy rápida (p. ej., consultas de Postgres).
  • Los enfoques de JIT ligeros —plantillas copy-and-patch, pequeñas bibliotecas (p. ej., SLJIT, GNU Lightning, AsmJit) o backends personalizados— sacrifican menos optimizaciones a cambio de un tiempo de compilación mucho menor.
  • Los benchmarks discutidos en las publicaciones enlazadas muestran que los JIT simples pueden lograr mejoras de velocidad significativas frente a la interpretación con un tiempo de compilación despreciable, mientras que LLVM puede pasar decenas de milisegundos compilando.
  • Los comentarios centrados en bases de datos subrayan que la optimización del plan de consulta (p. ej., el orden de los joins) importa mucho más que el JIT; la granularidad actual de JIT de Postgres (por expresión, no por pipeline) se ve como una limitación fundamental.

¿La generación de código basada en plantillas es “JIT real”?

  • Algunos descartan el enfoque como “solo plantillas de ensamblador”, pero otros replican que:
    • Los compiladores no optimizadores siguen siendo compiladores.
    • Copy-and-patch es una técnica JIT estándar y legítima.
  • Se dan ejemplos de generadores de código muy simples (sin asignación de registros, con mucho uso de pila) que solo son unas pocas veces más lentos que binarios muy optimizados -O3, pero mucho más rápidos que los intérpretes.

Common Lisp y JIT manual

  • Se discuten implementaciones de Common Lisp como un punto intermedio:
    • Algunas compilan todo de forma anticipada; otras admiten bytecode más compilación nativa opcional.
    • eval y la compilación al cargar se presentan como una forma de JIT.
    • Hay conmutadores para alternar entre intérprete y compilador en el uso de REPL, sacrificando tiempo de compilación a favor de velocidad de ejecución.
  • Se elogia un estilo de “JIT manual” —elegir explícitamente qué compilar para mejorar el rendimiento— como un compromiso práctico y más simple.

El papel de la IA en escribir JITs y sistemas complejos

  • Las opiniones divergen mucho:
    • Algunos dicen que los modelos actuales producen código pobre en dominios complejos, útil sobre todo para boilerplate, CRUD, conexión de APIs y detección simple de errores.
    • Otros informan grandes ganancias de productividad usando IA para esbozar componentes complejos (incluidos JITs), convirtiendo una semana de trabajo en horas, siempre que un ingeniero competente descomponga las tareas y revise la salida.
  • Hay debate sobre:
    • Cuánta experiencia de dominio sigue siendo necesaria (muchos dicen “mucha”).
    • Si la calidad del código generado por IA es objetivamente mala o solo estilísticamente distinta.
    • El riesgo de que usuarios sin experiencia publiquen código escrito por IA que parece decente superficialmente pero es frágil.

Otras aplicaciones y observaciones

  • Algunos comentaristas sugieren usar ideas similares de JIT basadas en plantillas para cortafuegos y generación dinámica de eBPF.
  • Se mencionan los motores de regex, los sistemas históricos de BASIC y Lisp, y los DBMS modernos como dominios clásicos o naturales para JIT.
  • El hilo también toca brevemente intérpretes verificados formalmente y binarios estáticos firmados como una alternativa de seguridad extrema frente a JIT y código nativo.