Python heeft een reputatie opgebouwd als krachtig, flexibel en eenvoudig om mee te werken. Deze deugden hebben geleid tot het gebruik van Python in een enorme en groeiende verscheidenheid aan toepassingen, workflows en vakgebieden. Maar het ontwerp van de taal – het geïnterpreteerde karakter, de runtime dynamiek – betekent dat Python altijd een orde van grootte langzamer is geweest dan machine-native talen zoals C of C++.

Over de jaren heen hebben ontwikkelaars een verscheidenheid aan workarounds voor Python’s snelheidsbeperkingen bedacht. Je zou bijvoorbeeld prestatie-intensieve taken in C kunnen schrijven en deze kunnen omwikkelen met Python; veel machine learning bibliotheken doen precies dit. Of je kunt Cython gebruiken, een project waarmee je Python-code kunt besprenkelen met runtime type-informatie, zodat het naar C kan worden gecompileerd.

Maar workarounds zijn nooit ideaal. Zou het niet geweldig zijn als we gewoon een bestaand Python programma konden nemen zoals het is, en het dramatisch sneller konden uitvoeren? Dat is precies wat PyPy je toestaat te doen.

PyPy vs. CPython

PyPy is een drop-in vervanger voor de standaard Python interpreter, CPython. Terwijl CPython Python compileert tot intermediaire bytecode die vervolgens wordt geïnterpreteerd door een virtuele machine, gebruikt PyPy just-in-time (JIT) compilatie om Python-code te vertalen naar machine-native assembleertaal.

Afhankelijk van de taak die wordt uitgevoerd, kan de prestatiewinst dramatisch zijn. Gemiddeld versnelt PyPy Python met ongeveer 7,6 keer, waarbij sommige taken 50 keer of meer worden versneld. De CPython interpreter voert gewoon niet dezelfde soort optimalisaties uit als PyPy, en zal dat waarschijnlijk ook nooit doen, omdat dat niet een van zijn ontwerpdoelen is.

Het beste deel is dat er weinig tot geen inspanning nodig is van de kant van de ontwikkelaar om de winst die PyPy biedt te ontgrendelen. Verwissel gewoon CPython voor PyPy, en voor het grootste deel ben je klaar. Er zijn een paar uitzonderingen, die hieronder worden besproken, maar PyPy’s verklaarde doel is om bestaande, ongewijzigde Python code te draaien en deze te voorzien van een automatische snelheidsboost.

PyPy ondersteunt momenteel zowel Python 2 als Python 3, door middel van verschillende incarnaties van het project. Met andere woorden, je moet verschillende versies van PyPy downloaden, afhankelijk van de versie van Python die je gaat draaien. De Python 2 tak van PyPy bestaat al veel langer, maar de Python 3 versie is de laatste tijd op snelheid gebracht. Het ondersteunt momenteel zowel Python 3.5 (productiekwaliteit) als Python 3.6 (bèta-kwaliteit).

Naast het ondersteunen van alle kern Python taal, werkt PyPy met de overgrote meerderheid van de tools in het Python ecosysteem, zoals pip voor verpakking of virtualenv voor virtuele omgevingen. De meeste Python pakketten, zelfs die met C modules, zouden as-is moeten werken, hoewel er beperkingen zijn waar we hieronder op in zullen gaan.

Hoe PyPy werkt

PyPy gebruikt optimalisatietechnieken die gevonden worden in andere just-in-time compilers voor dynamische talen. Het analyseert lopende Python programma’s om de type-informatie van objecten te bepalen terwijl ze worden gemaakt en gebruikt in programma’s, en gebruikt dan die type-informatie als een gids om dingen te versnellen. Bijvoorbeeld, als een Python-functie werkt met slechts een of twee verschillende objecttypen, genereert PyPy machinecode om die specifieke gevallen af te handelen.

PyPy’s optimalisaties worden automatisch afgehandeld tijdens runtime, dus je hoeft over het algemeen niet de prestaties te tweaken. Een gevorderde gebruiker zou kunnen experimenteren met PyPy’s command-line opties om snellere code te genereren voor speciale gevallen, maar slechts zelden is dit nodig.

PyPy wijkt ook af van de manier waarop CPython sommige interne functies behandelt, maar probeert compatibel gedrag te behouden. PyPy gaat bijvoorbeeld anders om met garbage collection dan CPython. Niet alle objecten worden onmiddellijk opgehaald zodra ze buiten scope gaan, dus een Python programma dat draait onder PyPy kan een grotere geheugen footprint vertonen dan wanneer het draait onder CPython. Maar u kunt nog steeds gebruik maken van Python’s high-level garbage collection controls blootgesteld via de gc module, zoals gc.enable(), gc.disable(), en gc.collect().

Als u informatie wilt over PyPy’s JIT gedrag tijdens runtime, PyPy bevat een module, pypyjit, die vele JIT hooks blootstelt aan uw Python applicatie. Als u een functie of module hebt die slecht lijkt te presteren met de JIT, kunt u met pypyjit gedetailleerde statistieken hierover verkrijgen.

Een andere PyPy-specifieke module, __pypy__, stelt andere functies bloot die specifiek zijn voor PyPy, dus kan nuttig zijn voor het schrijven van apps die gebruik maken van die functies. Vanwege Python’s runtime dynamiek, is het mogelijk om Python apps te bouwen die deze functies gebruiken wanneer PyPy aanwezig is en ze negeren wanneer dat niet het geval is.

PyPy beperkingen

Magisch als PyPy lijkt, het is geen magie. PyPy heeft bepaalde beperkingen die de effectiviteit voor bepaalde soorten programma’s verminderen of overbodig maken. Helaas is PyPy geen volledig universele vervanging voor de standaard CPython runtime.

PyPy werkt het beste met pure Python applicaties

PyPy heeft altijd het beste gepresteerd met “pure” Python applicaties – dat wil zeggen, applicaties geschreven in Python en niets anders. Python pakketten die een interface hebben met C bibliotheken, zoals NumPy, hebben het niet zo goed gedaan vanwege de manier waarop PyPy CPython’s native binaire interfaces emuleert.

PyPy’s ontwikkelaars hebben dit probleem aangepakt, en PyPy meer compatibel gemaakt met de meerderheid van Python pakketten die afhankelijk zijn van C extensies. Numpy, bijvoorbeeld, werkt nu heel goed met PyPy. Maar als je maximale compatibiliteit met C extensies wilt, gebruik dan CPython.

PyPy werkt het beste met langer lopende programma’s

Een van de neveneffecten van de manier waarop PyPy Python programma’s optimaliseert, is dat langer lopende programma’s het meest profiteren van de optimalisaties. Hoe langer het programma loopt, hoe meer runtime type informatie PyPy kan verzamelen, en hoe meer optimalisaties het kan maken. Eenmalige Python scripts zullen niet profiteren van dit soort dingen. De toepassingen die hier wel van profiteren hebben meestal loops die gedurende lange perioden draaien, of continu op de achtergrond draaien-web frameworks, bijvoorbeeld.

PyPy doet niet aan ahead-of-time compilatie

PyPy compileert Python code, maar het is geen compiler voor Python code. Vanwege de manier waarop PyPy zijn optimalisaties uitvoert en het inherente dynamisme van Python, is er geen manier om de resulterende JITted code als een standalone binary uit te zenden en opnieuw te gebruiken. Elk programma moet gecompileerd worden voor elke run. Als je Python wilt compileren in snellere code die kan draaien als een standalone app, gebruik dan Cython, Numba, of het momenteel experimentele Nuitka project.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.