Programmering

Cython tutorial: Hvordan øke hastigheten på Python

Python er et kraftig programmeringsspråk som er lett å lære og lett å jobbe med, men det er ikke alltid det raskeste å kjøre - spesielt når du arbeider med matte eller statistikk. Tredjepartsbiblioteker som NumPy, som pakker inn C-biblioteker, kan forbedre ytelsen til noen operasjoner betydelig, men noen ganger trenger du bare råhastigheten og kraften til C direkte i Python.

Cython ble utviklet for å gjøre det lettere å skrive C-utvidelser for Python, og for å tillate at eksisterende Python-kode transformeres til C. Dessuten lar Cython den optimaliserte koden sendes med et Python-program uten eksterne avhengigheter.

I denne opplæringen går vi gjennom trinnene som trengs for å transformere eksisterende Python-kode til Cython, og for å bruke den i en produksjonsapplikasjon.

Relatert video: Bruke Cython for å øke hastigheten på Python

Et Cython-eksempel

La oss begynne med et enkelt eksempel hentet fra Cythons dokumentasjon, en ikke veldig effektiv implementering av en integrert funksjon:

def f (x):

returner x ** 2-x

def integrate_f (a, b, N):

s = 0

dx = (b-a) / N

for jeg innen rekkevidde (N):

s + = f (a + i * dx)

returner s * dx

Koden er lett å lese og forstå, men den går sakte. Dette er fordi Python hele tiden må konvertere frem og tilbake mellom sine egne objekttyper og maskinens rå numeriske typer.

Vurder nå Cython-versjonen av den samme koden, med Cythons tillegg understreket:

 cdef f (dobbel x):

returner x ** 2-x

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

cdef int i

cdef dobbel s, x, dx

s = 0

dx = (b-a) / N

for jeg innen rekkevidde (N):

s + = f (a + i * dx)

returner s * dx

Disse tilleggene lar oss eksplisitt deklarere variable typer gjennom hele koden, slik at Cython-kompilatoren kan oversette de “dekorerte” tilleggene til C.

Relatert video: Hvordan Python gjør programmeringen enklere

Perfekt for IT, forenkler Python mange typer arbeid, fra systemautomatisering til arbeid i banebrytende felt som maskinlæring.

Cython-syntaks

Nøkkelordene som brukes til å dekorere Cython-kode, finnes ikke i konvensjonell Python-syntaks. De ble utviklet spesielt for Cython, så enhver kode dekorert med dem vil ikke kjøre som et konvensjonelt Python-program.

Dette er de vanligste elementene i Cythons syntaks:

Variable typer

Noen av de variable typene som brukes i Cython er ekko av Pythons egne typer, som f.eksint, flyte, og lang. Andre Cython-variabeltyper finnes også i C, som røye eller struct, som er erklæringer som usignert lang. Og andre er unike for Cython, som bint, en C-nivå representasjon av Python Sant / usant verdier.

De cdef og cpdef funksjonstyper

De cdef nøkkelord angir bruken av en Cython- eller C-type. Den brukes også til å definere funksjoner mye som du ville gjort i Python.

Funksjoner skrevet i Cython ved bruk av Python’s def nøkkelord er synlig for annen Python-kode, men medfører en ytelsesstraff. Funksjoner som bruker cdef nøkkelord er bare synlig for annen Cython- eller C-kode, men kjøres mye raskere. Hvis du har funksjoner som bare kalles internt fra en Cython-modul, bruk cdef.

Et tredje nøkkelord, cpdef, gir kompatibilitet med både Python-kode og C-kode, på en slik måte at C-kode kan få tilgang til den deklarerte funksjonen i full hastighet. Denne bekvemmeligheten koster imidlertid:cpdef funksjoner genererer mer kode og har litt mer samtaleomkostninger enn cdef.

Andre Cython-nøkkelord

Andre nøkkelord i Cython gir kontroll over aspekter av programflyt og atferd som ikke er tilgjengelig i Python:

  • gil og nogil. Dette er kontekstledere som brukes til å avgrense deler av koden som krever (med gil:) eller ikke krever (med nogil:) Pythons Global Interpreter Lock, eller GIL. C-kode som ikke ringer til Python API kan kjøre raskere i en nogil blokkering, spesielt hvis den utfører en langvarig operasjon som å lese fra en nettverkstilkobling.
  • cimportDette leder Cython til å importere C-datatyper, funksjoner, variabler og utvidelsestyper. Cython-apper som for eksempel bruker NumPys C-moduler cimport for å få tilgang til disse funksjonene.
  • inkludere. Dette plasserer kildekoden til en Cython-fil i en annen, på omtrent samme måte som i C. Merk at Cython har en mer sofistikert måte å dele erklæringer mellom Cython-filer enn bare inkluderes.
  • ctypedef. Brukes til å referere til typedefinisjoner i eksterne C-headerfiler.
  • ekstern. Brukt med cdef for å referere til C-funksjoner eller variabler som finnes i andre moduler.
  • offentlig / api. Brukes til å gjøre erklæringer i Cython-moduler som vil være synlige for annen C-kode.
  • på linje. Brukes for å indikere at en gitt funksjon skal være i linjen, eller ha koden plassert i kroppen til anropsfunksjonen når den brukes, for hastighets skyld. For eksempel f funksjonen i ovennevnte kodeeksempel kan dekoreres med på linje for å redusere funksjonsanropsoverhead, fordi det bare brukes ett sted. (Merk at C-kompilatoren kan utføre sin egen inlining automatisk, men på linje lar deg spesifisere eksplisitt om noe skal være inline.)

Det er ikke nødvendig å vite alle Cython-nøkkelordene på forhånd. Cython-kode skrives ofte trinnvis - først skriver du gyldig Python-kode, og deretter legger du til Cython-dekorasjon for å øke hastigheten. Dermed kan du plukke opp Cythons utvidede nøkkelordsyntaks stykkevis, etter hvert som du trenger det.

Kompilere Cython

Nå som vi har en ide om hvordan et enkelt Cython-program ser ut og hvorfor det ser ut som det ser ut, la oss gå gjennom trinnene som trengs for å kompilere Cython til en fungerende binær.

For å bygge et fungerende Cython-program trenger vi tre ting:

  1. Python-tolk. Bruk den siste versjonen, hvis du kan.
  2. Cython-pakken. Du kan legge Cython til Python ved hjelp av pip pakkeleder: pip installere cython
  3. En C-kompilator.

Vare nr. 3 kan være vanskelig hvis du bruker Microsoft Windows som utviklingsplattform. I motsetning til Linux kommer ikke Windows med en C-kompilator som en standardkomponent. For å løse dette, ta en kopi av Microsoft Visual Studio Community Edition, som inkluderer Microsofts C-kompilator og koster ingenting.

Vær oppmerksom på at den siste versjonen av Cython fra og med dette skrivet er 0.29.16, men en betaversjon av Cython 3.0 er tilgjengelig for bruk. Hvis du bruker pip installere cython, vil den nyeste ikke-betaversjonen installeres. Hvis du vil prøve betaversionen, bruk pip installer cython> = 3.0a1 for å installere den nyeste utgaven av Cython 3.0-grenen. Cythons utviklere anbefaler å prøve Cython 3.0-grenen når det er mulig, fordi det i noen tilfeller genererer betydelig raskere kode.

Cython-programmer bruker .pyx filutvidelse. Opprett en fil med navnet i en ny katalog num.pyx som inneholder Cython-kodeeksemplet vist ovenfor (det andre kodeeksemplet under "Et Cython-eksempel") og en fil som heter main.py som inneholder følgende kode:

fra num import integrate_f

skriv ut (integrate_f (1.0, 10.0, 2000))

Dette er et vanlig Python-program som vil kalle integrer_f funksjon funnet inum.pyx. Python-kode “ser” Cython-kode som bare en annen modul, så du trenger ikke å gjøre noe annet enn å importere den kompilerte modulen og kjøre dens funksjoner.

Til slutt, legg til en fil med navnet setup.py med følgende kode:

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

)

setup.py brukes vanligvis av Python for å installere modulen den er tilknyttet, og kan også brukes til å lede Python til å kompilere C-utvidelser for den modulen. Her bruker vi setup.py for å kompilere Cython-kode.

Hvis du bruker Linux, og du har installert en C-kompilator (vanligvis tilfelle), kan du kompilere .pyx fil til C ved å kjøre kommandoen:

python setup.py build_ext --inplace

Hvis du bruker Microsoft Windows og Microsoft Visual Studio 2017 eller bedre, må du sørge for at du har den nyeste versjonen av oppsettverktøy installert i Python (versjon 46.1.3 i skrivende stund) før den kommandoen fungerer. Dette sikrer at Pythons byggeverktøy vil kunne oppdage og bruke versjonen av Visual Studio du har installert automatisk.

Hvis kompileringen er vellykket, bør du se nye filer vises i katalogen: num.c (C-filen generert av Cython) og en fil med enten a .o utvidelse (på Linux) eller a .pyd utvidelse (på Windows). Det er den binære filen som C-filen er blitt samlet inn i. Du kan også se en \bygge underkatalog, som inneholder gjenstandene fra byggeprosessen.

Løpe python main.py, og du bør se noe i retning av følgende returnert som et svar:

283.297530375

Det er resultatet fra den kompilerte integrerte funksjonen, som påkalt av vår rene Python-kode. Prøv å spille med parametrene som sendes til funksjonen i main.py for å se hvordan utgangen endres.

Merk at når du gjør endringer i .pyx filen, må du kompilere den på nytt. (Eventuelle endringer du gjør i konvensjonell Python-kode vil tre i kraft med en gang.)

Den resulterende kompilerte filen har ingen avhengigheter bortsett fra versjonen av Python den ble kompilert for, og kan derfor pakkes sammen i et binært hjul. Merk at hvis du refererer til andre biblioteker i koden din, som NumPy (se nedenfor), må du oppgi dem som en del av applikasjonens krav.

Hvordan bruke Cython

Nå som du vet hvordan du kan "cythonisere" et stykke kode, er neste trinn å bestemme hvordan Python-applikasjonen din kan dra nytte av Cython. Hvor skal du bruke den?

For best resultat, bruk Cython for å optimalisere denne typen Python-funksjoner:

  1. Funksjoner som kjører i tette sløyfer, eller krever lange mengder behandlingstid på et enkelt "hot spot" med kode.
  2. Funksjoner som utfører numeriske manipulasjoner.
  3. Funksjoner som fungerer med objekter som kan vises i ren C, for eksempel grunnleggende numeriske typer, matriser eller strukturer, i stedet for Python-objekttyper som lister, ordbøker eller tupler.

Python har tradisjonelt vært mindre effektiv på sløyfer og numeriske manipulasjoner enn andre, ikke-tolket språk. Jo mer du dekorerer koden din for å indikere at den skal bruke numeriske basistyper som kan gjøres om til C, jo raskere vil den gjøre tallknusing.

Å bruke Python-objekttyper i Cython er ikke i seg selv et problem. Cython-funksjoner som bruker Python-objekter vil fortsatt kompilere, og Python-objekter kan være å foretrekke når ytelse ikke er det viktigste. Men enhver kode som bruker Python-objekter vil være begrenset av ytelsen til Python-kjøretiden, ettersom Cython vil generere kode for å adressere Pythons APIer og ABIer direkte.

Et annet verdig mål for Cython-optimalisering er Python-kode som samhandler direkte med et C-bibliotek. Du kan hoppe over Python "wrapper" -koden og grensesnittet med bibliotekene direkte.

Imidlertid gjør Cython detikke genererer automatisk de riktige anropsgrensesnittene for disse bibliotekene. Du må ha Cython henvise til funksjonssignaturene i bibliotekets headerfiler, ved hjelp av en cdef ekstern fra erklæring. Merk at hvis du ikke har topptekstfilene, er Cython tilgivende nok til å la deg erklære eksterne funksjonsunderskrifter som tilnærmer de originale topptekstene. Men bruk originalene når det er mulig for å være trygg.

Et eksternt C-bibliotek som Cython kan bruke rett ut av esken er NumPy. For å dra nytte av Cythons raske tilgang til NumPy-arrays, bruk cimport nummen (valgfritt med som np for å holde navnområdet sitt tydelig), og bruk deretter cdef uttalelser for å erklære NumPy-variabler, for eksempel cdef np.array eller np.arrangement.

Cython-profilering

Det første trinnet for å forbedre programmets ytelse er å profilere det - å generere en detaljert rapport om hvor tiden blir brukt under utførelsen. Python har innebygde mekanismer for å generere kodeprofiler. Cython henger ikke bare med i disse mekanismene, men har egne profileringsverktøy.

Pythons egen profil, cProfilgenererer rapporter som viser hvilke funksjoner som tar mest tid i et gitt Python-program. Som standard vises ikke Cython-kode i disse rapportene, men du kan aktivere profilering av Cython-kode ved å sette inn et kompileringsdirektiv øverst i .pyx fil med funksjoner du vil ha med i profileringen:

# cython: profile = True

Du kan også aktivere linje-for-linje-sporing av C-koden generert av Cython, men dette påfører mye overhead, og er derfor slått av som standard.

Vær oppmerksom på at profilering pålegger et ytelseshit, så sørg for å slå av profilering for kode som sendes i produksjon.

Cython kan også generere koderapporter som indikerer hvor mye av en gitt .pyx filen konverteres til C, og hvor mye av den som gjenstår Python-kode. For å se dette i aksjon, rediger setup.py filen i vårt eksempel og legg til følgende to linjer øverst:

importer Cython.Compiler.Options

Cython.Compiler.Options.annotate = Sant

(Alternativt kan du bruke et direktiv i setup.py for å aktivere merknader, men metoden ovenfor er ofte lettere å jobbe med.)

Slett .c filer generert i prosjektet og kjører setup.py manus for å kompilere alt. Når du er ferdig, bør du se en HTML-fil i samme katalog som deler navnet på .pyx-filen din - i dette tilfelletnum.html. Åpne HTML-filen, og du vil se de delene av koden din som fremdeles er avhengig av Python uthevet med gult. Du kan klikke på de gule områdene for å se den underliggende C-koden generert av Cython.

$config[zx-auto] not found$config[zx-overlay] not found