PHP 8.0: testiamolo con il compilatore JIT

PHP

Probabilmente avrete sentito la notizia di alcuni mesi fa che il compilatore JIT verrà aggiunto a PHP 8. Già dalla versione PHP 7.0, sono state apportate modifiche per migliorare le prestazioni, ma questa sarà una nuova svolta per PHP. Questa grande funzionalità è stata sviluppata 2 anni fa, ma il lancio di PHP 8.0 è previsto tra meno di 1 anno. In questo articolo, daremo un’occhiata di cosa si tratta realmente, ma soprattutto proveremo a testarlo su Linux.

Che cos’è JIT

Forse non conoscete JIT, quindi prima spiegheremo di cosa si tratta. Probabilmente sapete già che PHP è un linguaggio interpretato a scripting server side, il che significa che non è necessario compilare il codice prima che venga eseguito (a differenza di altri linguaggi come C/C++). Invece, il modulo PHP legge il codice e lo esegue. In altre parole, non si codifica il codice per compilarlo in linguaggio macchina per eseguirlo, ma viene codificato uno script e poi inviato a PHP per essere eseguito Server Side appunto.

PHP è dotato di una macchina virtuale chiamata Zend VM. Perché chiamarla macchina virtuale? Perché si comporta come un computer durante l’esecuzione del codice stesso. È responsabile della lettura e dell’esecuzione del codice PHP. Ma prima di ciò, il codice verrà letto e tradotto da PHP in opcode, la lingua che Zend VM comprende. Quindi Zend VM sarà in grado di eseguire quel codice operativo. Questa è un’illustrazione per una facile comprensione.

Quindi abbiamo bisogno prima di uno step di parse e poi di interpretazione. Velocizzeremo il tutto, sfruttando il cosiddetto OPCache (Opcode Cache) che memorizza i risultati della fase di compilazione, quindi non sarà necessario compilare nuovamente la volta successiva. Ecco come funzionava PHP fino ad ora.

Quindi ora parliamo del compilatore JIT, significa che compileremo la sorgente (codice) in un linguaggio macchina da eseguire. JIT sta per “Just-In-Time”, il che significa servirà tempo per compilare, invece di compilare prima e poi eseguire. Quando sarà necessario avviare lo script, sarà compilato subito al momento.

Per PHP, il compilatore JIT compilerà il codice sorgente, nel codice macchina ed eseguirà quel codice invece di inviarlo a Zend VM per essere eseguito. Quindi non abbiamo più bisogno di un interprete e, naturalmente, il codice funziona anche più velocemente.

Sarà davvero più veloce?

Questa è una demo che mostra la velocità di PHP con il compilatore JIT rispetto alla vecchia versione. Nel video si utilizza un codice per creare immagini 3D.

Ma ATTENZIONE, nessuno ha mai usato PHP per creare immagini 3D

Poiché il nostro script sarà compilato nel codice macchina e sarà eseguito direttamente dalla CPU anziché dall’interprete Server Side, gli aumenti di velocità riguarderanno principalmente l’avvio del codice stesso e l’utilizzo della CPU (quindi richiede potenza della macchina). Ma l’applicazione Web non avendo tale codice, il tutto sarà delegato dal processo dall’I/O (query del database, caricamento del file, richiesta HTTP ecc.), il più delle volte l’app non fa nulla, resta solo in ascolto per mostrare i risultati dal database o dall’API, ecc. Purtroppo, ora come ora una webapp difficilmente sarà più veloce.

A cosa serve la JIT?

A partire dalla versione PHP 7.0, il problema delle prestazioni è stato uno delle grosse problematiche da risolvere, in parte anche grazie alla concorrenza dell’HHVM di Facebook (pure utilizza il compilatore JIT). OPCache, struttura i dati, ottimizzando tutto poco a poco per ottenere le massime prestazioni.

Inoltre, le prestazioni di PHP per un linguaggio server possono già essere considerate abbastanza buone, e non sarà più lento come in passato. Quindi è tempo di espandere un le capacità di PHP, verso aree come l’analisi dei dati, il rendering 3D/2D, ecc.

In passato, il codice ad alte prestazioni veniva spesso scritto come estensione in C/C++ anziché come pacchetto PHP. Ad esempio, la phpredis è sempre 6-7 volte più veloce di predis. Se il codice PHP viene compilato anziché interpretato, otterremo pacchetti con le stesse prestazioni delle estensioni scritte in C/C++.

Ecco il motivo principale per cui è stato scelto il compilatore JIT: questa è la migliore e potenziale direzione verso quale dirigersi.

Proviamoci: facciamo un test

Dopo questa doverosa introduzione, proviamolo direttamente su campo. Dal momento che non esiste ancora una versione di PHP 8.0, dovremo compilare dal codice sorgente PHP versione 7.4 ossia ancora in alpha, il master è già 8.0. Innanzitutto, scarichiamo il codice sorgente:

wget -O php.zip https://github.com/php/php-src/archive/master.zip unzip php.zip cd php-src-master

Quindi installiamo le dipendenze. Useremo Ubuntu Server, se usate altre distro troverete lo stesso pacchetto da soli.

apt-get install autoconf bison dpkg-dev file g++ gcc libc-dev make pkg-config re2c libxml2-dev libsqlite3-dev

Generiamo il file di build ./buildconf;

Quindi eseguire ./configure per impostare la build. Selezionate le opzioni per compilare PHP. Puoi scegliere un percorso da installare aggiungendo –prefix = <dir_install>. Di default installerà /usr/local/bin, quindi se avete già installato un’altra versione, ricordatevi di configurarlo. PHP verrà installato nel percorso <dir_install>/bin/php. Aggiungete il percorso per il file di configurazione con –with-config-file-path e –with-config-file-scan-dir se lo desiderate.

./configure --help 

Ad esempio, eseguo ./configure come di seguito:

./configure --prefix=/opt --with-config-file-path=/opt/php –with-config-file-scan-dir=/opt/php/conf.d

Montiamo

make -j$(nproc)
make install

Quindi controlliamo eseguendo :

php -v

da shell dovrebbe risultare:

PHP 8.0.0-dev (cli) (built: Jul 15 2019 02:22:59) ( NTS ) 
Copyright (c) The PHP Group Zend Engine v4.0.0-dev, 
opyright (c) Zend Technologies     
with Zend OPcache v8.0.0-dev, Copyright (c), by Zend Technologies

Il compilatore JIT fa parte dell’estensione OPCache, quindi abilitiamolo prima di utilizzarlo. Aggiungiamo queste righe al file php.ini:

opcache.jit_buffer_size=32M
opcache.jit=1235

Ora proveremo a eseguire un file PHP con l’opzione

-dopcache.jit_debug = 1

Vedremo il codice assembly compilato. Ad esempio, con un file come questo:

<?php

$a = 0;

for ($i = 0; $i < 10000; $i++) {
$a += $i;
}

echo $a;

Sarà compilato nel codice assembly come di seguito:

JIT$/php-src-master/hello.php: ; (/php-src-master/hello.php)
         sub $0x10, %rsp
         lea 0x50(%r14), %rdi
         cmp $0xa, 0x8(%rdi)
         jnz .L1
         mov (%rdi), %rdi
         cmp $0x0, 0x18(%rdi)
         jnz .L12
         add $0x8, %rdi
 .L1:
         test $0x1, 0x9(%rdi)
         jnz .L13
 .L2:
         mov $0x0, (%rdi)
         mov $0x4, 0x8(%rdi)
  ; ...
  

Ora proviamo il benchmark per vedere se realmente è più veloce. Nel codice sorgente, c’è un file Zend/bench.php che possiamo testare. In questo file ci sono molti codici calcolati (hash, loop .etc). Questo è il risultato ottenuto (riformattato per un facile confronto):

Sicuramente risulta più veloce, non è affatto strano. Proveremo a confrontare un’altra app web, un progetto Laravel e confrontato con lo strumento ApacheBench

ab -t10 -c10 http://test.localhost/

Questo è il risultato ottenuto:

PHP 7.3: 131.37 req/s
PHP 8.0 + JIT: 133.57 req/s

In breve , dalla versione 8 sarà significativamente più veloce, ma per il momento, la maggior parte del codice PHP esistente nel mondo, manterrà degli standard che risultano ancora approssimativamente lenti. Tuttavia, nuovi sviluppi si apriranno per la nuova versione PHP compilata JIT.

Se avete bisogno di maggiori aggiornamenti, oppure chiarimenti su come configurare adeguatamente un server web, non esitate a contattarmi in privato, sono in grado di risolvere qualunque problema riscontriate nelle vostre configurazioni.

Non affidatevi al caso, ma in tema di Sviluppo Applicazioni Web (in questo caso WebApp Development) affidatevi sempre ad una Persona di Competenza.