Cos'è PyPy? Python più veloce senza dolore

Python si è guadagnato la reputazione di essere potente, flessibile e facile da lavorare. Queste virtù hanno portato al suo utilizzo in una varietà enorme e crescente di applicazioni, flussi di lavoro e campi. Ma il design del linguaggio - la sua natura interpretata, il suo dinamismo di runtime - significa che Python è sempre stato un ordine di grandezza più lento dei linguaggi nativi delle macchine come C o C ++.

Nel corso degli anni, gli sviluppatori hanno escogitato una serie di soluzioni alternative per i limiti di velocità di Python. Ad esempio, potresti scrivere attività ad alta intensità di prestazioni in C e avvolgerle con Python; molte librerie di machine learning fanno esattamente questo. Oppure potresti usare Cython, un progetto che ti consente di cospargere il codice Python con informazioni sul tipo di runtime che ne consentono la compilazione in C.

Ma le soluzioni alternative non sono mai l'ideale. Non sarebbe fantastico se potessimo semplicemente prendere un programma Python esistente così  com'è ed eseguirlo notevolmente più velocemente? Questo è esattamente ciò che PyPy ti permette di fare.

Video correlato: utilizzo del runtime PyPy per Python

PyPy contro CPython

PyPy è un sostituto immediato dell'interprete Python di serie, CPython. Mentre CPython compila Python in bytecode intermedio che viene quindi interpretato da una macchina virtuale, PyPy utilizza la compilazione just-in-time (JIT) per tradurre il codice Python in linguaggio assembly nativo della macchina.

A seconda dell'attività svolta, i miglioramenti delle prestazioni possono essere notevoli. In media, PyPy accelera Python di circa 7,6 volte, con alcune attività accelerate 50 volte o più. L'interprete CPython semplicemente non esegue gli stessi tipi di ottimizzazioni di PyPy, e probabilmente non lo farà mai, poiché questo non è uno dei suoi obiettivi di progettazione.

La parte migliore è che è richiesto uno sforzo minimo da parte dello sviluppatore per sbloccare i guadagni forniti da PyPy. Sostituisci semplicemente CPython con PyPy e per la maggior parte hai finito. Ci sono alcune eccezioni, discusse di seguito, ma l'obiettivo dichiarato di PyPy è quello di eseguire codice Python esistente e non modificato e fornirgli un aumento automatico della velocità.

PyPy attualmente supporta sia Python 2 che Python 3, attraverso diverse incarnazioni del progetto. In altre parole, è necessario scaricare diverse versioni di PyPy a seconda della versione di Python che verrà eseguita. Il ramo Python 2 di PyPy esiste da molto più tempo, ma la versione Python 3 è stata aggiornata di recente. Attualmente supporta sia Python 3.5 (qualità di produzione) che Python 3.6 (qualità beta).

Oltre a supportare tutto il linguaggio Python di base, PyPy funziona con la stragrande maggioranza degli strumenti nell'ecosistema Python, come  pip per il packaging o  virtualenv per gli ambienti virtuali. La maggior parte dei pacchetti Python, anche quelli con moduli C, dovrebbero funzionare così come sono, anche se ci sono limitazioni che approfondiremo di seguito.

Come funziona PyPy

PyPy utilizza tecniche di ottimizzazione trovate in altri compilatori just-in-time per linguaggi dinamici. Analizza l'esecuzione di programmi Python per determinare le informazioni sul tipo degli oggetti quando vengono creati e utilizzati nei programmi, quindi utilizza tali informazioni sul tipo come guida per accelerare le cose. Ad esempio, se una funzione Python funziona solo con uno o due diversi tipi di oggetti, PyPy genera codice macchina per gestire quei casi specifici.

Le ottimizzazioni di PyPy vengono gestite automaticamente in fase di esecuzione, quindi in genere non è necessario modificarne le prestazioni. Un utente avanzato potrebbe sperimentare con le opzioni della riga di comando di PyPy per generare codice più veloce per casi speciali, ma solo raramente è necessario.

PyPy si discosta anche dal modo in cui CPython gestisce alcune funzioni interne, ma cerca di preservare comportamenti compatibili. Ad esempio, PyPy gestisce la garbage collection in modo diverso da CPython. Non tutti gli oggetti vengono raccolti immediatamente una volta che escono dall'ambito, quindi un programma Python in esecuzione sotto PyPy potrebbe mostrare un'impronta di memoria maggiore rispetto a quando viene eseguito in CPython. Ma è ancora possibile utilizzare ad alto livello controlli garbage collection di Python liberi attraverso l' gcunità, come ad esempio gc.enable(), gc.disable()e gc.collect().

Se desideri informazioni sul comportamento JIT di PyPy in fase di esecuzione, PyPy include un modulo pypyjit, che espone molti hook JIT alla tua applicazione Python. Se si dispone di una funzione o di un modulo che sembra funzionare male con il JIT, pypyjitconsente di ottenere statistiche dettagliate su di esso.

Un altro modulo __pypy__specifico di PyPy, espone altre funzionalità specifiche di PyPy, quindi può essere utile per scrivere app che sfruttano tali funzionalità. A causa del dinamismo di runtime di Python, è possibile costruire app Python che utilizzano queste funzionalità quando PyPy è presente e le ignora quando non lo è.

Limitazioni di PyPy

Per quanto magico possa sembrare PyPy, non è magico. PyPy ha alcune limitazioni che riducono o annullano la sua efficacia per determinati tipi di programmi. Purtroppo, PyPy non è un sostituto completamente universale per il runtime CPython di serie.

PyPy funziona meglio con le app Python pure

PyPy ha sempre funzionato al meglio con le applicazioni Python "pure", ovvero le applicazioni scritte in Python e nient'altro. I pacchetti Python che si interfacciano con le librerie C, come NumPy, non sono andati altrettanto bene a causa del modo in cui PyPy emula le interfacce binarie native di CPython. 

Gli sviluppatori di PyPy si sono ridotti a questo problema e hanno reso PyPy più compatibile con la maggior parte dei pacchetti Python che dipendono dalle estensioni C. Numpy, ad esempio, ora funziona molto bene con PyPy. Ma se vuoi la massima compatibilità con le estensioni C, usa CPython.

PyPy funziona meglio con i programmi più lunghi

Uno degli effetti collaterali del modo in cui PyPy ottimizza i programmi Python è che i programmi con esecuzione più lunga beneficiano maggiormente delle sue ottimizzazioni. Più a lungo viene eseguito il programma, più informazioni sul tipo di runtime PyPy può raccogliere e più ottimizzazioni può apportare. Gli script Python completi non trarranno vantaggio da questo genere di cose. Le applicazioni che ne traggono vantaggio hanno in genere cicli che vengono eseguiti per lunghi periodi di tempo o vengono eseguiti continuamente in background, ad esempio i framework web.

PyPy non esegue la compilazione anticipata

PyPy  compila  codice Python, ma non è  un compilatore  per codice Python. A causa del modo in cui PyPy esegue le sue ottimizzazioni e del dinamismo intrinseco di Python, non c'è modo di emettere il codice JITted risultante come binario autonomo e riutilizzarlo. Ogni programma deve essere compilato per ogni esecuzione. Se vuoi compilare Python in un codice più veloce che possa essere eseguito come app standalone, usa Cython, Numba o il progetto Nuitka attualmente sperimentale.