Programmering

Cython tutorial: Sådan fremskyndes Python

Python er et stærkt programmeringssprog, der er let at lære og let at arbejde med, men det er ikke altid det hurtigste at køre - især når du har at gøre med matematik eller statistik. Tredjepartsbiblioteker som NumPy, der indpakker C-biblioteker, kan forbedre ydeevnen for nogle operationer markant, men nogle gange har du bare brug for den rå hastighed og kraft C direkte i Python.

Cython blev udviklet for at gøre det lettere at skrive C-udvidelser til Python og tillade, at eksisterende Python-kode omdannes til C. Desuden tillader Cython, at den optimerede kode sendes med en Python-applikation uden eksterne afhængigheder.

I denne vejledning gennemgår vi de nødvendige trin til at omdanne eksisterende Python-kode til Cython og bruge den i en produktionsapplikation.

Relateret video: Brug af Cython til at fremskynde Python

Et Cython-eksempel

Lad os begynde med et simpelt eksempel taget fra Cythons dokumentation, en ikke særlig effektiv implementering af en integreret funktion:

def f (x):

returnere x ** 2-x

def integrere_f (a, b, N):

s = 0

dx = (b-a) / N

for jeg inden for rækkevidde (N):

s + = f (a + i * dx)

returner s * dx

Koden er let at læse og forstå, men den kører langsomt. Dette skyldes, at Python konstant skal konvertere frem og tilbage mellem sine egne objekttyper og maskinens rå numeriske typer.

Overvej nu Cython-versionen af ​​den samme kode, med Cythons tilføjelser understreget:

 cdef f (dobbelt x):

returnere x ** 2-x

def integrate_f (dobbelt a, dobbelt b, int N):

cdef int i

cdef dobbelt s, x, dx

s = 0

dx = (b-a) / N

for jeg inden for rækkevidde (N):

s + = f (a + i * dx)

returner s * dx

Disse tilføjelser giver os mulighed for eksplicit at erklære variable typer i hele koden, så Cython-kompilatoren kan oversætte de "dekorerede" tilføjelser til C.

Relateret video: Hvordan Python gør programmeringen nemmere

Python er perfekt til IT og forenkler mange slags arbejde, lige fra systemautomatisering til arbejde inden for banebrydende felter som maskinindlæring.

Cythonsyntaks

Nøgleordene, der bruges til at dekorere Cython-kode, findes ikke i konventionel Python-syntaks. De blev udviklet specielt til Cython, så enhver kode, der er dekoreret med dem, kører ikke som et konventionelt Python-program.

Dette er de mest almindelige elementer i Cythons syntaks:

Variable typer

Nogle af de variable typer, der anvendes i Cython, er ekkoer af Pythons egne typer, f.eksint, flydeog lang. Andre Cython-variabeltyper findes også i C, ligesom char eller struct, ligesom erklæringer som usigneret lang. Og andre er unikke for Cython, ligesom bint, en C-niveau repræsentation af Python Sandt falsk værdier.

Det cdef og cpdef funktionstyper

Det cdef nøgleord angiver brugen af ​​en Cython- eller C-type. Det bruges også til at definere funktioner meget som i Python.

Funktioner skrevet i Cython ved hjælp af Pythons def nøgleord er synlige for anden Python-kode, men pådrager sig en præstationsstraff. Funktioner, der bruger cdef nøgleord er kun synlige for anden Cython- eller C-kode, men udføres meget hurtigere. Hvis du har funktioner, der kun kaldes internt fra et Cython-modul, skal du bruge cdef.

Et tredje nøgleord cpdef, giver kompatibilitet med både Python-kode og C-kode på en sådan måde, at C-kode kan få adgang til den deklarerede funktion ved fuld hastighed. Denne bekvemmelighed har en pris, dog:cpdef funktioner genererer mere kode og har lidt mere opkaldsudgifter end cdef.

Andre Cython nøgleord

Andre nøgleord i Cython giver kontrol over aspekter af programflow og adfærd, der ikke er tilgængelig i Python:

  • gil og nogil. Disse er kontekstadministratorer, der bruges til at afgrænse sektioner af kode, der kræver (med gil:) eller ikke kræver (med nogil:) Pythons Global Interpreter Lock eller GIL. C-kode, der ikke foretager opkald til Python API, kan køre hurtigere i en nogil blok, især hvis den udfører en langvarig operation, såsom læsning fra en netværksforbindelse.
  • cimportDette leder Cython til at importere C-datatyper, funktioner, variabler og udvidelsestyper. Cython-apps, der f.eks. Bruger NumPys native C-moduler cimport for at få adgang til disse funktioner.
  • omfatte. Dette placerer kildekoden for en Cython-fil inde i en anden, på samme måde som i C. Bemærk at Cython har en mere sofistikeret måde at dele erklæringer mellem Cython-filer ud over bare omfattes.
  • ctypedef. Bruges til at henvise til typedefinitioner i eksterne C-headerfiler.
  • ekstern. Brugt med cdef for at henvise til C-funktioner eller variabler, der findes i andre moduler.
  • offentlig / api. Bruges til at afgive erklæringer i Cython-moduler, der vil være synlige for andre C-koder.
  • inline. Brugt til at angive, at en given funktion skal være inline, eller have dens kode placeret i kroppen af ​​den opkaldende funktion, hver gang den bruges af hensyn til hastighed. For eksempel f funktion i ovenstående kodeeksempel kunne blive dekoreret med inline for at reducere dets funktionsopkaldsudgifter, fordi det kun bruges ét sted. (Bemærk, at C-kompilatoren muligvis automatisk udfører sin egen inline, men inline lader dig angive eksplicit, om noget skal være inline.)

Det er ikke nødvendigt at kende alle Cython-nøgleordene på forhånd. Cython-kode har tendens til at skrives trinvist - først skriver du gyldig Python-kode, derefter tilføjer du Cython-dekoration for at fremskynde den. Således kan du hente Cythons udvidede nøgleordssyntaks stykkevis, efterhånden som du har brug for det.

Kompilér Cython

Nu hvor vi har en idé om, hvordan et simpelt Cython-program ser ud, og hvorfor det ser ud som det ser ud, lad os gå gennem de nødvendige trin for at kompilere Cython til en fungerende binær.

For at opbygge et fungerende Cython-program har vi brug for tre ting:

  1. Python-tolken. Brug den seneste version, hvis du kan.
  2. Cython-pakken. Du kan tilføje Cython til Python ved hjælp af pip pakkehåndtering: pip installere cython
  3. En C-kompilator.

Vare nr. 3 kan være vanskelig, hvis du bruger Microsoft Windows som din udviklingsplatform. I modsætning til Linux leveres Windows ikke med en C-compiler som en standardkomponent. For at løse dette skal du tage en kopi af Microsoft Visual Studio Community Edition, som inkluderer Microsofts C-compiler og koster intet.

Bemærk, at den seneste udgivelse af Cython fra denne skrivning er 0.29.16, men en betaversion af Cython 3.0 er tilgængelig til brug. Hvis du bruger pip installere cython, installeres den nyeste version, der ikke er betaversion. Hvis du vil prøve betaversionen, skal du bruge pip installere cython> = 3.0a1 at installere den seneste udgave af Cython 3.0-filialen. Cythons udviklere anbefaler at prøve Cython 3.0-grenen, når det er muligt, fordi det i nogle tilfælde genererer betydeligt hurtigere kode.

Cython-programmer bruger .pyx filtypenavn. I en ny mappe skal du oprette en fil med navnet num.pyx der indeholder Cython-kodeeksemplet vist ovenfor (den anden kodeeksempel under “Et Cython-eksempel”) og en fil med navnet main.py der indeholder følgende kode:

fra num import integrate_f

udskriv (integrer_f (1.0, 10.0, 2000))

Dette er et almindeligt Python-program, der kalder integrer_f funktion findes inum.pyx. Python-kode “ser” Cython-kode som bare et andet modul, så du behøver ikke gøre noget andet end at importere det kompilerede modul og køre dets funktioner.

Endelig tilføj en fil med navnet setup.py med følgende kode:

fra distutils.core importopsætning fra distutils.extension import Extension fra Cython.Build import cythonize ext_modules = [Extension (r'num ', [r'num.pyx']),] setup (name = "num", ext_modules = cythonize (ekst_moduler),

)

setup.py bruges normalt af Python til at installere det modul, det er tilknyttet, og kan også bruges til at lede Python til at kompilere C-udvidelser til dette modul. Her bruger vi setup.py at kompilere Cython-kode.

Hvis du bruger Linux, og du har installeret en C-compiler (typisk tilfældet), kan du kompilere .pyx fil til C ved at køre kommandoen:

python setup.py build_ext --inplace

Hvis du bruger Microsoft Windows og Microsoft Visual Studio 2017 eller bedre, skal du sørge for at have den nyeste version af opsætningsværktøjer installeret i Python (version 46.1.3 i skrivende stund), før den kommando fungerer. Dette sikrer, at Pythons byggeværktøjer er i stand til automatisk at registrere og bruge den version af Visual Studio, du har installeret.

Hvis kompileringen er vellykket, skal du se nye filer vises i biblioteket: num.c (C-filen genereret af Cython) og en fil med enten a .o udvidelse (på Linux) eller en .pyd udvidelse (på Windows). Det er den binære fil, som C-filen er blevet kompileret i. Du kan også se en \ build underkatalog, som indeholder artefakterne fra byggeprocessen.

Løb python main.py, og du skulle se noget i retning af følgende returneret som et svar:

283.297530375

Det er output fra den kompilerede integrerede funktion, som påkaldt af vores rene Python-kode. Prøv at spille med de parametre, der sendes til funktionen i main.py for at se, hvordan output ændres.

Bemærk, at når du foretager ændringer i .pyx fil, skal du kompilere den igen. (Eventuelle ændringer, du foretager i konventionel Python-kode, træder i kraft med det samme.)

Den resulterende kompilerede fil har ingen afhængigheder undtagen den version af Python, den blev kompileret til, og kan derfor bundtes i et binært hjul. Bemærk, at hvis du henviser til andre biblioteker i din kode, som NumPy (se nedenfor), skal du angive dem som en del af applikationens krav.

Sådan bruges Cython

Nu hvor du ved, hvordan du "cythoniserer" et stykke kode, er det næste trin at bestemme, hvordan din Python-applikation kan drage fordel af Cython. Hvor skal du anvende det nøjagtigt?

For de bedste resultater skal du bruge Cython til at optimere disse slags Python-funktioner:

  1. Funktioner, der kører i stramme sløjfer eller kræver lange behandlingstider i et enkelt "hot spot" kode.
  2. Funktioner, der udfører numeriske manipulationer.
  3. Funktioner, der fungerer med objekter, der kan repræsenteres i ren C, såsom grundlæggende numeriske typer, arrays eller strukturer, snarere end Python-objekttyper som lister, ordbøger eller tupler.

Python har traditionelt været mindre effektiv til sløjfer og numeriske manipulationer end andre ikke-fortolkede sprog. Jo mere du dekorerer din kode for at angive, at den skal bruge numeriske basistyper, der kan omdannes til C, jo hurtigere vil det gøre antallet-knasende.

Brug af Python-objekttyper i Cython er ikke i sig selv et problem. Cython-funktioner, der bruger Python-objekter, kompileres stadig, og Python-objekter kan være at foretrække, når ydeevne ikke er den største overvejelse. Men enhver kode, der gør brug af Python-objekter, begrænses af Pythons runtime, da Cython genererer kode, der direkte adresserer Pythons API'er og ABI'er.

Et andet værdigt mål for Cython-optimering er Python-kode, der interagerer direkte med et C-bibliotek. Du kan springe Python "wrapper" -koden over og grænsefladen direkte til bibliotekerne.

Imidlertid gør Cython detikke genererer automatisk de rette opkaldsgrænseflader til disse biblioteker. Du skal have Cython henvise til funktionsunderskrifterne i bibliotekets headerfiler ved hjælp af en cdef ekstern fra erklæring. Bemærk, at hvis du ikke har headerfiler, er Cython tilgivende nok til at lade dig erklære eksterne funktionsunderskrifter, der tilnærmer sig de originale overskrifter. Men brug originalerne, når det er muligt, for at være sikker.

Et eksternt C-bibliotek, som Cython kan bruge lige ud af kassen, er NumPy. Brug for at udnytte Cythons hurtige adgang til NumPy-arrays cimport bedøvet (valgfrit med som np for at holde sit navneområde forskelligt), og brug derefter cdef udsagn for at erklære NumPy-variabler, såsom cdef np.array eller np.ndarray.

Cython-profilering

Det første skridt til at forbedre en applikations ydeevne er at profilere den - at generere en detaljeret rapport om, hvor tiden bruges under udførelsen. Python leverer indbyggede mekanismer til generering af kodeprofiler. Cython hænger ikke kun ind i disse mekanismer, men har egne profileringsværktøjer.

Pythons egen profil, cProfilgenererer rapporter, der viser, hvilke funktioner der tager mest tid i et givet Python-program. Som standard vises Cython-kode ikke i disse rapporter, men du kan aktivere profilering af Cython-kode ved at indsætte et compiler-direktiv øverst i .pyx fil med funktioner, du vil medtage i profileringen:

# cython: profil = Sandt

Du kan også aktivere linje-for-linjesporing på C-koden genereret af Cython, men dette pålægger en masse overhead, og det er som standard slået fra.

Bemærk, at profilering pålægger et performance hit, så sørg for at skifte profilering til kode, der sendes til produktion.

Cython kan også generere koderapporter, der angiver, hvor meget af en given .pyx fil konverteres til C, og hvor meget af den forbliver Python-kode. For at se dette i aktion skal du redigere setup.py fil i vores eksempel og tilføj følgende to linjer øverst:

importer Cython.Compiler.Options

Cython.Compiler.Options.annotate = Sandt

(Alternativt kan du bruge et direktiv i setup.py for at aktivere kommentarer, men ovenstående metode er ofte lettere at arbejde med.)

Slet .c filer, der er genereret i projektet og kører setup.py script til at kompilere alt. Når du er færdig, skal du se en HTML-fil i samme bibliotek, der deler navnet på din .pyx-fil - i dette tilfældenum.html. Åbn HTML-filen, og du vil se de dele af din kode, der stadig er afhængige af Python, fremhævet med gult. Du kan klikke på de gule områder for at se den underliggende C-kode genereret af Cython.