JIT en PHP: Qué es y cómo se utiliza

PHP

El compilador JIT viene de la mano de PHP 8. El objetivo de JIT es el de acelerar la ejecución de los scripts creados con PHP. En este tutorial vamos a ver qué es JIT y cómo puede ayudarte a mejor la velocidad de casi algunos proyectos creados con PHP. Toda tecnología tiene tanto ventajas como inconvenientes, por lo que en este tutorial también nos adentraremos en las dificultades que te podrías encontrar cuando lo utilices.

Qué es JIT

El nombre de JIT deriva de las siglas de «JUST IN TIME». PHP es un lenguaje interpretado, por lo que a diferencia de otros lenguajes compilados como C o Java, el código PHP se traduce a código máquina en tiempo de ejecución, de modo que el procesador sea capaz de entenderlo. Realizar esta tarea en tiempo real es bastante pesada, por lo que el rendimiento de los lenguajes interpretados no es obviamente el mismo que el de los lenguajes que exigen que su código sea compilado antes de ser ejecutado.

Lo que hace JIT es compilar ciertas partes del código en tiempo de ejecución, de modo que el código compilado pueda ser usado en sucesivas ocasiones en detrimento del código PHP sin compilar. JIT actúa del mismo modo que un sistema de caché que minifica y reduce el tamaño de los scripts en tiempo de ejecución, sirviendo dichos archivos en el futuro. En este caso, lo que se usa es el código máquina compilado.

Esto se consigue mediante un monitor que observa el código durante su ejecución. Cuando el monitor detecta que ciertas partes del código se ejecutan múltiples veces, marcará dichas partes como candidatas a ser compiladas. Estas porciones del código, también denominadas zonas calientes, pueden ser compiladas y servidas en forma de código máquina cuando se vuelvan a ejecutar en el futuro. De este modo, el rendimiento de los scripts PHP mejora considerablemente, ya que varias porciones de su código son ejecutadas al vuelo.

Aunque este proceso pueda parecer bastante obvio, las dificultades que entraña son tremendas. De hecho, los compiladores JIT existen para montones de lenguajes de programación interpretados, pero no siempre se utilizan debido a los problemas que conllevan. Aunque el compilador JIT de PHP ha madurado mucho, sus ventajas todavía son discutibles con respecto a sus inconvenientes.

Compatibilidad de JIT

El compilador JIT es multiplataforma, por lo que por ahora, funciona tanto en Linux como en Windows y Mac. Esto es algo muy a tener en cuenta que abre las puertas a que sea usado a gran escala en un futuro.

En cuanto a las versiones de PHP, JIT fue propuesto para ser usado en PHP 8, aunque también existe una versión experimental que funciona con PHP 7.4.

Los problemas de JIT

El compilador JIT intentará encontrar las porciones de tu código que se ejecutan varias veces. Sin embargo, en donde más se utiliza PHP es en el mundo del desarrollo web, por lo que las mejoras en el rendimiento no son tan grandes como podrían ser en otros ámbitos como el cálculo matemático o la generación de fractales, cuyos cálculos se repiten una y otra vez.

Cuando usas el compilador JIT con una aplicación web, no es habitual que el código se repita tantas veces, por lo que el compilador apenas encontrará zonas clave para compilar. De hecho, uno de los objetivos que tenemos en cuenta al procesar una petición HTTP con PHP es el de optimizar el tiempo de respuesta y evitar repetir las mismas tareas una y otra vez. Esto no significa que no vayamos a obtener mejoras de rendimiento, pero distarán bastante de las mejoras tan espectaculares que se obtienen con otro tipo de procesos.

Sin embargo, esto no significa que debamos descartar por completo el uso de JIT. De hecho, JIT abre las puertas a que PHP sea usado en muchos otros ámbitos fuera del mundo del desarrollo web en los que actualmente se descarta. Además, el compilador JIT de PHP mejora día a día, por lo que es de esperar que pronto resulte útil en otros ámbitos.

El mayor problema de JIT es que su salida es código máquina, por lo que de haber algún error, no obtendremos las excepciones de PHP que tanta información nos aportan, sino una serie de errores ininteligibles para la mayor parte de los programadores que acostumbran a usar lenguajes de programación de alto nivel. Podríamos intentar encontrar a alguien que nos ayude, pero por ahora, son muy pocas las personas que han trabajado en el compilador JIT. De hecho, casi todo el trabajo involucrado con el compilador ha sido realizado por una sola persona: Dmitry Stogov.

El hecho de que no exista todavía una comunidad sólida de usuarios y desarrolladores que usen JIT implica que su uso a nivel comercial esté prácticamente descartado en el caso de la mayoría de los mortales. Podrías utilizar JIT, ayudar a mantenerlo e involucrarte en el proyecto, pero no es algo sencillo si acostumbras a trabajar con lenguajes de alto nivel. De lo contrario, de haber algún problema, quizás tengas que esperar un tiempo considerable a que aparezca una actualización que lo solucione.

Configuración de JIT

Puedes instalar JIT y hacerlo funcionar con PHP 7.4, aunque es un proceso bastante completo. Sin embargo, JIT viene incluido en el núcleo de PHP 8. La activación y configuración de JIT es una tarea relativamente sencilla, ya que funciona muy bien con su configuración por defecto. Para activar JIT consulta el siguiente tutorial:

En general, basta con editar el archivo php.ini y a activar el compilador asignando el valor de 64M a la opción opcache.jit_buffer_size:

opcache.jit_buffer_size = 64M

Además, asignaremos el valor tracing a la opción opcache.jit, que es su valor por defecto, equivalente a utilizar 1255 como valor:

opcache.jit = tracing

Para asegurarte de que JIT está realmente funcionando, puedes ejecutar la siguiente función, que mostrará un array asociativo que debería tener asignado el valor true para la clave enabled:

var_dump(opcache_get_status()['jit']);

Concurrencia con JIT

En teoría no debería ser necesario ejecutar varios procesos concurrentemente para observar las ventajas de JIT, ya que es capaz de optimizar un único proceso que se repita varias veces. Sin embargo, tu servidor sí debería ser capaz de atender más peticiones HTTP cuando usas JIT, por lo que si tenemos esto en cuenta, sí tendría sentido ejecutar los procesos en concurrencia.

Rendimiento de JIT

Vamos a realizar ciertas pruebas para comprobar si el uso del compilador JIT de PHP es realmente útil en el ámbito del desarrollo de aplicaciones web. Esta guía ha sido creada con fines ilustrativos, aunque si quieres, puedes seguirla. Sin embargo, primero tendrás que instalar PHP 8 y configurar JIT de modo que funcione en un entorno de servidor.

Utilizando Apache Bench, vamos a lanzar varias peticiones HTTP a la vez a un script de nuestro servidor utilizando PHP FPM, configurado para que no se superen los 20 procesos concurrentes.

Importante: Si vas a probar este proceso y PHP 8 todavía no está disponible, tendrás que compilarl PHP mediante el siguiente comando:

./configure' '--prefix=/opt/php/php-src' '--with-config-file-path=/opt/php/php-8.0a1/etc' '--enable-mbstring' '--enable-zip' '--enable-bcmath' '--enable-pcntl' '--enable-ftp' '--enable-exif' '--enable-calendar' '--enable-sysvmsg' '--enable-sysvsem' '--enable-sysvshm' '--enable-wddx' '--with-curl' '--with-mcrypt' '--with-iconv' '--with-gmp' '--with-pspell' '--with-gd' '--with-jpeg-dir=/usr' '--with-png-dir=/usr' '--with-zlib-dir=/usr' '--with-xpm-dir=/usr' '--with-freetype-dir=/usr' '--with-t1lib=/usr' '--enable-gd-native-ttf' '--enable-gd-jis-conv' '--with-openssl' '--with-pdo-mysql=mysqlnd' '--with-gettext=/usr' '--with-zlib=/usr' '--with-bz2=/usr' '--with-recode=/usr' '--with-mysqli=mysqlnd'

Los mayores beneficios de de JIT se observan con la generación de fractales, por lo que vamos a utilizar el siguiente código, que es el mismo que podemos encontrar en la documentación oficial, en la que se compara su ejecución en Java con su ejecución en PHP haciendo uso de JIT:

public function iteracion($x, $y)
{
  $cr = $y - 0.5;
  $ci = $x;
  $zi = 0.0;
  $zr = 0.0;
  $i = 0;

  while (true) {
    $i++;
    
    $temp = $zr * $zi;
    
    $zr2 = $zr * $zr;
    $zi2 = $zi * $zi;
    
    $zr = $zr2 - $zi2 + $cr;
    $zi = $temp + $temp + $ci;

    if ($zi2 + $zr2 > 16) return $i;

    if ($i > 5000) return 0;
  }
}

Esta sería la función principal del test:

public function index()
{
  for ($y = -39; $y < 39; $y++) {
    printf("\n");

    for ($x = -39; $x < 39; $x++) {
      $i = $this->iteracion($x / 40.0, $y / 40.0);

      if ($i == 0) printf("*");
      else printf(" ");
    }
  }
  printf("\n");
}

Ahora vamos a ejecutar el test utilizando Apache Bench con el siguiente comando:

ab -n 100 -c 20 -l http://mi-dominio.tld:8081/test

Estos serían los resultados:

  • Ejecución sin JIT: 3.53 peticiones por segundo.
  • Ejecución con JIT: 40.60 peticiones por segundo.

Las ventajas de utilizar JIT parecen evidentes cuando realizamos tareas de cálculo. Sin embargo, cuando usamos un framework como Zend Framework o Laravel, o algún otro framework de PHP, las ventajas no están tan claras.

Vamos a crear un proceso que lea una serie de posts de una base de datos y los muestre secuencialmente en una lista utilizando un proceso recursivo. Existirán también otras tareas propias de cada framework como la gestión de un ORM como Doctrine o Eloquent, el enrutamiento o la gestión del contenedor que inyecta las dependencias. El rendimiento es el siguiente:

  • Ejecución sin JIT: 62.42 peticiones por segundo.
  • Ejecución con JIT: 68.29 peticiones por segundo.

Tal y como podemos comprobar, el rendimiento es casi el mismo, y si asignamos el valor function a la opción opcache.jit de JIT, el rendimiento es todavía inferior. Además, en caso de que aumentes la memoria que puede utilizar el proceso, el rendimiento con JIT es incluso inferior, por lo que su uso es una desventaja.

El compilador JIT tiene un gran potencial, pero tal y como podremos comprobar, actualmente los grandes beneficios de JIT se obtienen exclusivamente con grandes tareas de cálculo. Salvo que realices cálculos complejos, JIT no resulta útil en la ejecución de procesos típicos del desarrollo web.

Si quieres, puedes acceder a este repositorio de GitHub para ejecutar el test que se explica aquí, en el que se comprueba el rendimiento de JIT usando Doctrine.

Conclusión

JIT no ofrece todavía demasiadas ventajas en el mundo del desarrollo web debido a su naturaleza, aunque lo cierto es que es de esperar que aparezcan mejoras muy pronto. Sin embargo, sí podría resultar muy útil en una arquitectura de microservicios, con ciertas tareas muy concretas en donde existen procesos de cálculo muy pesados. JIT resulta especialmente beneficioso con la generación de fractales y con el renderizado gráficos.

JIT abre las puertas a que PHP sea usado como lenguaje de propósito general en otros ámbitos que actualmente están restringidos a otros lenguajes como Node o Python. Por primera vez, PHP podría competir en rendimiento con muchos otros lenguajes de programación, manteniendo además la facilidad de mantenimiento y uso inherente a PHP.

Si quieres, puedes consultar la especificación de JIT en este enlace.


Avatar de Edu Lazaro

Edu Lázaro: Ingeniero técnico en informática, actualmente trabajo como desarrollador web y programador de videojuegos.

👋 Hola! Soy Edu, me encanta crear cosas y he redactado esta guía. Si te ha resultado útil, el mayor favor que me podrías hacer es el de compatirla en Twitter 😊

Si quieres conocer mis proyectos, sígueme en Twitter.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *