Python și-a câștigat reputația de a fi puternic, flexibil și ușor de utilizat. Aceste virtuți au dus la utilizarea sa într-o varietate imensă și în creștere de aplicații, fluxuri de lucru și domenii. Dar designul limbajului – natura sa interpretată, dinamismul său în timpul execuției – înseamnă că Python a fost întotdeauna cu un ordin de mărime mai lent decât limbajele native pentru mașini, cum ar fi C sau C++.
De-a lungul anilor, dezvoltatorii au venit cu o varietate de soluții pentru limitările de viteză ale lui Python. De exemplu, ați putea scrie sarcini de performanță intensivă în C și să le înfășurați cu Python; multe biblioteci de învățare automată fac exact acest lucru. Sau ați putea folosi Cython, un proiect care vă permite să presărați codul Python cu informații de tip de execuție care îi permit să fie compilat în C.
Dar soluțiile de rezolvare nu sunt niciodată ideale. Nu ar fi grozav dacă am putea să luăm un program Python existent așa cum este, și să îl rulăm dramatic mai repede? Exact asta este ceea ce PyPy vă permite să faceți.
PyPyPy vs. CPython
PyPy este un înlocuitor direct al interpretorului Python standard, CPython. În timp ce CPython compilează Python în bytecode intermediar care este apoi interpretat de o mașină virtuală, PyPy folosește compilarea just-in-time (JIT) pentru a traduce codul Python în limbaj de asamblare nativ pentru mașină.
În funcție de sarcina care se execută, câștigurile de performanță pot fi dramatice. În medie, PyPy accelerează Python de aproximativ 7,6 ori, unele sarcini fiind accelerate de 50 de ori sau mai mult. Interpretul CPython pur și simplu nu realizează aceleași tipuri de optimizări ca PyPy și, probabil, nu o va face niciodată, deoarece acesta nu este unul dintre obiectivele sale de proiectare.
Cea mai bună parte este că este necesar un efort mic sau deloc din partea dezvoltatorului pentru a debloca câștigurile pe care le oferă PyPy. Pur și simplu schimbați CPython cu PyPy și, în cea mai mare parte, ați terminat. Există câteva excepții, discutate mai jos, dar scopul declarat al lui PyPy este de a rula codul Python existent, nemodificat, și de a-i oferi o creștere automată a vitezei.
PyPyPy suportă în prezent atât Python 2, cât și Python 3, prin intermediul diferitelor încarnări ale proiectului. Cu alte cuvinte, trebuie să descărcați versiuni diferite de PyPy în funcție de versiunea de Python pe care o veți rula. Ramura Python 2 a PyPy există de mult mai mult timp, dar versiunea Python 3 a fost adusă la zi în ultima vreme. În prezent, aceasta suportă atât Python 3.5 (calitate de producție), cât și Python 3.6 (calitate beta).
Pe lângă faptul că suportă toate elementele de bază ale limbajului Python, PyPy funcționează cu marea majoritate a instrumentelor din ecosistemul Python, cum ar fi pip
pentru împachetare sau virtualenv
pentru medii virtuale. Majoritatea pachetelor Python, chiar și cele cu module C, ar trebui să funcționeze ca atare, deși există limitări pe care le vom analiza mai jos.
Cum funcționează PyPyPy
PyPyPy folosește tehnici de optimizare găsite în alte compilatoare just-in-time pentru limbaje dinamice. Acesta analizează programele Python în execuție pentru a determina informațiile de tip ale obiectelor pe măsură ce acestea sunt create și utilizate în programe, apoi folosește aceste informații de tip ca un ghid pentru a accelera lucrurile. De exemplu, dacă o funcție Python lucrează doar cu unul sau două tipuri diferite de obiecte, PyPy generează cod mașină pentru a gestiona aceste cazuri specifice.
Optimizările lui PyPyPy sunt gestionate automat în timpul execuției, astfel încât, în general, nu este nevoie să îi modificați performanța. Un utilizator avansat ar putea experimenta cu opțiunile din linia de comandă a lui PyPy pentru a genera cod mai rapid pentru cazuri speciale, dar numai rareori este necesar acest lucru.
PyPyPy se îndepărtează, de asemenea, de modul în care CPython tratează unele funcții interne, dar încearcă să păstreze comportamente compatibile. De exemplu, PyPy gestionează colectarea gunoiului în mod diferit față de CPython. Nu toate obiectele sunt colectate imediat după ce ies din domeniul de aplicare, astfel încât un program Python care rulează sub PyPy poate prezenta o amprentă de memorie mai mare decât atunci când rulează sub CPython. Dar puteți utiliza în continuare controalele de colectare a gunoiului de nivel înalt ale Python expuse prin modulul gc
, cum ar fi gc.enable()
, gc.disable()
și gc.collect()
.
Dacă doriți informații despre comportamentul JIT al PyPy în timpul execuției, PyPy include un modul, pypyjit
, care expune multe cârlige JIT pentru aplicația dumneavoastră Python. Dacă aveți o funcție sau un modul care pare să aibă performanțe slabe cu JIT, pypyjit
vă permite să obțineți statistici detaliate despre acesta.
Un alt modul specific PyPy, __pypy__
, expune alte caracteristici specifice PyPy, astfel încât poate fi util pentru a scrie aplicații care valorifică aceste caracteristici. Datorită dinamismului din timpul de execuție al lui Python, este posibil să se construiască aplicații Python care utilizează aceste caracteristici atunci când PyPy este prezent și le ignoră atunci când nu este prezent.
Limitări ale PyPyPy
Pe cât de magic ar putea părea PyPy, acesta nu este magic. PyPy are anumite limitări care îi reduc sau îi anulează eficiența pentru anumite tipuri de programe. Din păcate, PyPy nu este un înlocuitor complet universal pentru timpul de execuție CPython stoc.
PyPy funcționează cel mai bine cu aplicații Python pure
PyPy a funcționat întotdeauna cel mai bine cu aplicații Python „pure” – adică aplicații scrise în Python și nimic altceva. Pachetele Python care au interfață cu biblioteci C, cum ar fi NumPy, nu s-au descurcat la fel de bine din cauza modului în care PyPy emulează interfețele binare native ale CPython.
Dezvoltatorii lui PyPyPy au eliminat această problemă și au făcut PyPy mai compatibil cu majoritatea pachetelor Python care depind de extensii C. Numpy, de exemplu, funcționează foarte bine cu PyPy acum. Dar dacă doriți o compatibilitate maximă cu extensiile C, folosiți CPython.
PyPy funcționează cel mai bine cu programe care rulează mai mult timp
Unul dintre efectele secundare ale modului în care PyPy optimizează programele Python este că programele care rulează mai mult timp beneficiază cel mai mult de optimizările sale. Cu cât programul rulează mai mult timp, cu atât mai multe informații de tip run-time PyPy poate aduna și cu atât mai multe optimizări poate face. Scripturile Python care se execută o singură dată nu vor beneficia de acest tip de lucru. Aplicațiile care beneficiază de obicei au bucle care rulează pentru perioade lungi de timp sau care rulează continuu în fundal – cadre web, de exemplu.
PyPyPy nu face compilare înainte de timp
PyPyPy compilează cod Python, dar nu este un compilator pentru cod Python. Din cauza modului în care PyPy își realizează optimizările și a dinamismului inerent al Python, nu există nicio modalitate de a emite codul JIT rezultat ca un binar de sine stătător și de a-l reutiliza. Fiecare program trebuie să fie compilat pentru fiecare execuție. Dacă doriți să compilați Python în cod mai rapid care poate rula ca aplicație independentă, utilizați Cython, Numba sau proiectul Nuitka, în prezent experimental.
.