1 puntos por GN⁺ 2025-11-19 | 1 comentarios | Compartir por WhatsApp
  • YJIT y ZJIT son arquitecturas de compiladores JIT en Ruby 3.x que convierten código Ruby a lenguaje máquina para aumentar la velocidad de ejecución
  • YJIT cuenta cuántas veces se llama cada función o bloque y, al llegar a cierto umbral, convierte ese código a lenguaje máquina
  • El código convertido se guarda en bloques YJIT, y cada bloque transforma varias instrucciones YARV en instrucciones de lenguaje máquina ARM64
  • Usa Branch Stubs para observar en tiempo de ejecución los tipos de datos reales y generar selectivamente las instrucciones de lenguaje máquina adecuadas
  • Esta estructura es un mecanismo clave para lograr al mismo tiempo mejor rendimiento de ejecución de Ruby y eficiencia al manejar tipos dinámicos

Capítulo 4: Compilar Ruby a lenguaje máquina

Interpreting vs. Compiling Ruby Code

  • No hay detalles en el texto original

Counting Method and Block Calls

  • YJIT rastrea la cantidad de llamadas a funciones y bloques del programa para identificar código hotspot
    • Junto a la secuencia de instrucciones YARV de cada función o bloque, almacena los valores jit_entry y jit_entry_calls
    • jit_entry es inicialmente null y más adelante guarda el puntero al código máquina generado por YJIT
    • jit_entry_calls aumenta en 1 cada vez que hay una llamada
  • Cuando la cantidad de llamadas alcanza el umbral, YJIT compila ese código a lenguaje máquina
    • En Ruby 3.5, el umbral predeterminado es de 30 llamadas en programas pequeños y 120 llamadas en aplicaciones grandes
    • Puede modificarse en tiempo de ejecución con la opción --yjit-call-threshold
  • Con este enfoque, YJIT convierte a lenguaje máquina solo el código que se ejecuta con frecuencia para asegurar una ruta de ejecución eficiente

YJIT Blocks

  • YJIT guarda las instrucciones de lenguaje máquina que genera en bloques YJIT
    • Un bloque YJIT es distinto de un bloque Ruby y corresponde a una sección parcial de instrucciones YARV
    • Cada función o bloque Ruby está compuesto por varios bloques YJIT
  • En el programa de ejemplo, cuando el bloque se ejecuta por trigésima vez, YJIT empieza a compilar
    • Convierte la primera instrucción YARV getlocal_WC_1 a lenguaje máquina y crea un nuevo bloque YJIT
    • Después compila la instrucción getlocal_WC_0 y la incluye en el mismo bloque
  • Según la Figura 4-8, YJIT genera instrucciones ARM64 para cargar valores en los registros x1 y x9 del procesador M1
    • getlocal_WC_1 guarda en la pila una variable local del frame de pila anterior, y getlocal_WC_0 una variable del frame de pila actual
    • Las instrucciones de lenguaje máquina generadas realizan la misma operación

YJIT Branch Stubs

  • Cuando YJIT compila la instrucción opt_plus, surge el problema de no poder conocer el tipo de los operandos
    • Según el tipo, como entero, cadena o punto flotante, se requieren distintas instrucciones de lenguaje máquina
    • Por ejemplo, la suma de enteros usa la instrucción adds, mientras que la suma de punto flotante necesita otra instrucción
  • Para resolverlo, YJIT usa observación en tiempo de ejecución en lugar de análisis previo
    • Mientras el programa corre, verifica los tipos reales de los valores recibidos y genera el código máquina correspondiente
  • Para este comportamiento usa Branch Stubs
    • Cuando una nueva rama (branch) todavía no tiene un bloque conectado, se enlaza temporalmente a un stub
    • Después, cuando se confirma el tipo real, ese stub se reemplaza por el bloque apropiado

ZJIT (solo se menciona)

  • El índice incluye una sección relacionada con ZJIT, pero el cuerpo del texto no ofrece una explicación concreta

Resumen

  • YJIT es un compilador JIT para Ruby 3.5 orientado a mejorar la eficiencia de ejecución de un lenguaje de tipos dinámicos
  • Sus puntos clave son el disparador de compilación basado en cantidad de llamadas, la estructura de bloques YJIT y la verificación de tipos en tiempo de ejecución mediante Branch Stubs
  • En la arquitectura ARM64, convierte el código a instrucciones reales de lenguaje máquina para mejorar la velocidad de ejecución del código Ruby
  • ZJIT se menciona como un JIT de próxima generación, pero el texto no entra en detalles

1 comentarios

 
GN⁺ 2025-11-19
Comentarios de Hacker News
  • Hace tiempo existió MacRuby, que usaba LLVM para compilar a código nativo en macOS e integrarse con frameworks de Objective‑C
    Era una idea bastante genial, pero al final parece que Apple cambió de rumbo hacia Swift
    Si sale una nueva versión, pienso comprar y leer sí o sí Ruby Under a Microscope. Ruby todavía me gusta mucho, aunque no he tenido muchas oportunidades de usarlo en la práctica

    • Después de que el creador de MacRuby dejó Apple, creó RubyMotion
      Ahora otras personas lo continúan, pero da la impresión de que hoy están más enfocados en DragonRuby (una implementación de Ruby centrada en juegos)
    • Después de que su autor se fue, MacRuby continuó como RubyMotion
      Como referencia, también está este artículo de Wikipedia
    • Incluso ahora se pueden crear apps para macOS, iOS y iPadOS usando Objective‑C
      Aunque puede que algunas APIs antiguas ya no tengan soporte
    • Viéndolo desde la perspectiva de alguien que ha trabajado con varios lenguajes, que Apple se haya pasado a Swift se siente un poco como cuando Microsoft pasó de VB6 a VB.Net
      VB6 era realmente rapidísimo para desarrollar, y hasta permitía trabajar con Direct3D y ASP Classic
      La elegancia y facilidad de desarrollo de Ruby me recuerdan esa época
      Si Ruby hubiera tenido herramientas GUI al nivel de VB6, creo que su popularidad habría sido bastante distinta
  • Da mucho gusto ver que Pat sigue adelante con el proyecto
    Su primer libro Ruby Under a Microscope y sus publicaciones del blog fueron una gran inspiración para mí
    Incluso llegué a conocerlo en persona en la conferencia Euruko, y de verdad era una persona excelente

    • Gracias por el comentario tan cálido
  • La primera vez que leí Ruby Under a Microscope me divertí muchísimo
    Gracias a eso incluso lo aproveché antes para resolver retos de CTF
    Últimamente no he seguido la implementación interna de Ruby, pero si sale una nueva versión, definitivamente la voy a comprar

    • Usé mucho Ruby de 2002 a 2010, pero después casi lo dejé por completo
      Al ver este artículo, me dieron ganas de volver a leer la nueva edición del libro
  • Ya que salió el tema de compilar Ruby, me pregunto si alguien ha probado el compilador Sorbet hecho por desarrolladores de Stripe
    Publicación sobre la liberación de Sorbet Compiler

    • Lamentablemente, ahora desapareció del repositorio y parece que ya no se sigue desarrollando
      La compilación AOT es realmente difícil en Ruby
      Lo interesante del enfoque de Sorbet es que puede crear rutas rápidas basadas en la verificación de tipos de Ruby
      Yo también estoy haciendo un compilador de Ruby como proyecto personal, y estoy tomando como referencia hokstad.com/compiler y
      writing-a-compiler-in-ruby
      Por ahora estoy concentrado en pasar RubySpec, y más adelante quiero probar optimizaciones basadas en tipos
  • No está directamente relacionado con compilar Ruby, pero el libro Enterprise Integration with Ruby me dio mucha perspectiva sobre cómo usar Ruby fuera del mundo web

  • Desde que conocí MRuby, me he enganchado a la diversión de convertir mis proyectos y scripts en ejecutables independientes

  • Me alegra que Ruby Under a Microscope siga actualizándose
    Creo que es una lectura obligatoria para cualquiera que quiera entender cómo funciona Ruby por dentro

  • Siempre tuve curiosidad sobre cómo YJIT rastrea la compilación por tipo de entrada cuando un bloque se ejecuta varias veces
    Quería entender cómo Ruby maneja distintos tipos como int o float

    • Ese es justamente el núcleo de YJIT
      Usa un enfoque de “esperar y ver” que retrasa la compilación hasta que se proporcionan tipos reales
      Mantiene versiones separadas del bloque para cada tipo y llama la adecuada según la situación
      A este algoritmo se le llama Basic Block Versioning
      Maxime Chevalier‑Boisvert de Shopify lo explica muy bien en su charla de RubyConf 2021
      El nuevo motor JIT, ZJIT, parece usar un enfoque diferente
  • Hacer que un lenguaje de tipado dinámico sea rápido con JIT normalmente tiene como costo un mayor uso de memoria
    Si no eres una empresa grande como Shopify, eso podría ser un problema todavía mayor

    • Pero las empresas pequeñas normalmente también tienen aplicaciones más pequeñas
      Hoy en día, las instancias en la nube suelen dar alrededor de 4GiB de memoria por núcleo, así que unos cientos de MB de código JIT son perfectamente manejables
  • Me pareció simple que YJIT encuentre hotspots contando solo cuántas veces se llama una función
    Me preguntaba si no tendría algo como los JIT de JavaScript, que detectan operaciones pesadas dentro de los bucles
    La estructura de bloques de Ruby quizá podría ayudar con ese tipo de optimización

    • Exacto, Ruby trata el cuerpo de los bucles como bloques, así que
      el JIT puede manejar esos bloques como si fueran funciones separadas y optimizar naturalmente los bucles repetitivos
      Ese tema se va a tratar con más profundidad en el siguiente capítulo