Hvordan optimalisere Python-skriptene dine for bedre ytelse

Hvordan Optimalisere Python Skriptene Dine For Bedre Ytelse



Å optimalisere Python-skriptene for bedre ytelse innebærer å identifisere og adressere flaskehalsene i koden vår, slik at den kjører raskere og mer effektivt. Python er et populært og kraftig programmeringsspråk som brukes i en rekke applikasjoner i dag, inkludert dataanalyse, ML-prosjekter (maskinlæring), webutvikling og mange flere. Python-kodeoptimalisering er en strategi for å forbedre utviklerprogrammets hastighet og effektivitet når du utfører enhver aktivitet ved å bruke færre linjer med kode, mindre minne eller ekstra ressurser. Stor og ineffektiv kode kan bremse programmet ned, noe som kan føre til dårlig kundetilfredshet og potensielt økonomisk tap, eller behov for mer arbeid for å fikse og feilsøke.

Det er nødvendig mens du utfører en oppgave som krever behandling av flere handlinger eller data. Derfor kan det å bytte ut og forbedre noen ineffektive kodeblokker og funksjoner ha fantastiske resultater som følgende:

  1. Øk applikasjonens ytelse
  2. Lag lesbar og organisert kode
  3. Gjør feilovervåking og feilsøking enklere
  4. Spar betydelig regnekraft og så videre

Profiler koden din

Før vi begynner å optimalisere, er det viktig å identifisere delene av prosjektkoden som bremser den. Teknikkene for profilering i Python inkluderer cProfile og profilpakkene. Bruk slike verktøy for å måle hvor raskt visse funksjoner og kodelinjer utføres. cProfile-modulen produserer en rapport som beskriver hvor lang tid hver skriptfunksjon tar å kjøre. Denne rapporten kan hjelpe oss med å finne funksjoner som kjører sakte, slik at vi kan forbedre dem.







Kodebit:



import cProfil som cP
def beregne Sum ( inputNumber ) :
sum_of_input_numbers = 0
samtidig som inputNumber > 0 :
sum_of_input_numbers + = inputNumber % 10
inputNumber // = 10
skrive ut ( 'Summen av alle sifre i inndatanummeret er: 'sum_of_input_numbers'' )
komme tilbake sum_of_input_numbers
def main_func ( ) :
cP. løpe ( 'calculateSum(9876543789)' )
hvis __Navn__ == '__hoved__' :
main_func ( )

Programmet foretar totalt fem funksjonskall som vist på den første linjen i utdataene. Detaljene for hvert funksjonskall vises i de følgende få linjene, inkludert antall ganger funksjonen ble påkalt, den totale varigheten av tiden i funksjonen, varigheten av tiden per anrop og den totale tiden i funksjonen (inkludert alle funksjonene det kalles).



I tillegg skriver programmet ut en rapport på ledetekstskjermen som viser at programmet fullfører utførelsestiden for alle oppgavene innen 0.000 sekunder. Dette viser hvor raskt programmet er.





Velg riktig datastruktur

Ytelseskarakteristikker er avhengig av datastrukturen. Spesielt er ordbøker raskere for oppslag enn lister angående generell lagring. Velg den datastrukturen som er best egnet for operasjonene vi skal utføre på dataene dine hvis du kjenner til disse. Følgende eksempel undersøker effektiviteten til forskjellige datastrukturer for en identisk prosess for å avgjøre om et element i datastrukturen er tilstede.



Vi evaluerer tiden det tar å sjekke om et element er til stede i hver datastruktur – en liste, et sett og en ordbok – og sammenligner dem.

OptimizeDataType.py:

import Timei som tt
import tilfeldig som rndobj
# Generer en liste over heltall
random_data_list = [ rndobj. randint ( 1 , 10 000 ) til _ i område ( 10 000 ) ]
# Lag et sett fra de samme dataene
tilfeldig_datasett = sett ( random_data_list )

# Lag en ordbok med samme data som nøkler
obj_DataDictionary = { på en: Ingen til på en i random_data_list }

# Element å søke etter (finnes i dataene)
tilfeldig_tall_å_finne = rndobj. valg ( random_data_list )

# Mål tiden for å sjekke medlemskap i en liste
liste_tid = tt. Timei ( lambda : tilfeldig_tall_å_finne i random_data_list , Antall = 1000 )

# Mål tiden for å sjekke medlemskap i et sett
set_time = tt. Timei ( lambda : tilfeldig_tall_å_finne i tilfeldig_datasett , Antall = 1000 )

# Mål tiden for å sjekke medlemskap i en ordbok
dikt_tid = tt. Timei ( lambda : tilfeldig_tall_å_finne i obj_DataDictionary , Antall = 1000 )

skrive ut ( f 'Sjekktid for listemedlemskap: {list_time:.6f} sekunder' )
skrive ut ( f 'Angi sjekktid for medlemskap: {set_time:.6f} sekunder' )
skrive ut ( f 'Kontrolltid for ordbokmedlemskap: {dict_time:.6f} sekunder' )

Denne koden sammenligner ytelsen til lister, sett og ordbøker når du utfører medlemskontroller. Generelt er sett og ordbøker betydelig raskere enn lister for medlemskapstester fordi de bruker hash-baserte oppslag, så de har en gjennomsnittlig tidskompleksitet på O(1). Lister, derimot, må gjøre lineære søk som resulterer i medlemskapstester med O(n) tidskompleksitet.

  Et skjermbilde av en datamaskin Beskrivelse generert automatisk

Bruk de innebygde funksjonene i stedet for løkker

Tallrike innebygde funksjoner eller metoder i Python kan brukes til å utføre typiske oppgaver som filtrering, sortering og kartlegging. Å bruke disse rutinene i stedet for å lage ens løkker bidrar til å øke hastigheten på koden fordi de ofte er ytelsesoptimalisert.

La oss bygge litt eksempelkode for å sammenligne ytelsen til å lage tilpassede løkker ved å bruke de innebygde funksjonene for typiske jobber (som map(), filter() og sorted()). Vi vil evaluere hvor godt de ulike kartleggings-, filtrerings- og sorteringsmetodene fungerer.

BuiltInFunctions.py:

import Timei som tt
# Eksempelliste over tallliste
tallliste = liste ( område ( 1 , 10 000 ) )

# Funksjon til square numbers_list ved hjelp av en løkke
def square_using_loop ( tallliste ) :
square_result = [ ]
til på en i tallliste:
square_result. legge til ( på en ** 2 )
komme tilbake square_result
# Funksjon for å filtrere partallsliste ved hjelp av en løkke
def filter_even_using_loop ( tallliste ) :
filter_result = [ ]
til på en i tallliste:
hvis på en % 2 == 0 :
filter_result. legge til ( på en )
komme tilbake filter_result
# Funksjon for å sortere tallliste ved hjelp av en løkke
def sort_using_loop ( tallliste ) :
komme tilbake sortert ( tallliste )
# Mål tiden til square numbers_list ved hjelp av map()
map_time = tt. Timei ( lambda : liste ( kart ( lambda x: x ** 2 , tallliste ) ) , Antall = 1000 )
# Mål tiden for å filtrere partallsliste ved hjelp av filter()
filter_time = tt. Timei ( lambda : liste ( filter ( lambda x: x % 2 == 0 , tallliste ) ) , Antall = 1000 )
# Mål tiden for å sortere numbers_list ved å bruke sorted()
sortert_tid = tt. Timei ( lambda : sortert ( tallliste ) , Antall = 1000 )
# Mål tiden til square numbers_list ved hjelp av en løkke
loop_map_time = tt. Timei ( lambda : square_using_loop ( tallliste ) , Antall = 1000 )
# Mål tiden for å filtrere partallsliste ved hjelp av en løkke
loop_filter_time = tt. Timei ( lambda : filter_even_using_loop ( tallliste ) , Antall = 1000 )
# Mål tiden for å sortere tallliste ved hjelp av en løkke
loop_sorted_time = tt. Timei ( lambda : sort_using_loop ( tallliste ) , Antall = 1000 )
skrive ut ( 'Nummerliste inneholder 10 000 elementer' )
skrive ut ( f 'Map() Tid: {map_time:.6f} sekunder' )
skrive ut ( f 'Filter() Tid: {filter_time:.6f} sekunder' )
skrive ut ( f 'Sortert() Tid: {sorted_time:.6f} sekunder' )
skrive ut ( f 'Sløyfe (kart) tid: {loop_map_time:.6f} sekunder' )
skrive ut ( f 'Sløyfe (filter) tid: {loop_filter_time:.6f} sekunder' )
skrive ut ( f 'Sløyfe (sortert) tid: {loop_sorted_time:.6f} sekunder' )

Vi vil sannsynligvis observere at de innebygde funksjonene (map(), filter() og sorted()) er raskere enn de egendefinerte løkkene for disse vanlige oppgavene. De innebygde funksjonene i Python tilbyr en mer kortfattet og forståelig tilnærming til å utføre disse oppgavene og er svært optimalisert for ytelse.

Optimaliser løkkene

Hvis det er nødvendig å skrive løkkene, er det noen få teknikker vi kan gjøre for å øke hastigheten. Vanligvis er range()-løkken raskere enn å iterere bakover. Dette er fordi range() genererer en iterator uten å invertere listen, noe som kan være en kostbar operasjon for lange lister. I tillegg, siden range() ikke bygger en ny liste i minnet, bruker den mindre minne.

OptimizeLoop.py:

import Timei som tt
# Eksempelliste over tallliste
tallliste = liste ( område ( 1 , 100 000 ) )
# Funksjon for å iterere over listen i omvendt rekkefølge
def loop_reverse_iteration ( ) :
resultat_omvendt = [ ]
til j i område ( bare ( tallliste ) - 1 , - 1 , - 1 ) :
resultat_omvendt. legge til ( tallliste [ j ] )
komme tilbake resultat_omvendt
# Funksjon for å iterere over listen ved å bruke range()
def loop_range_iteration ( ) :
resultatområde = [ ]
til k i område ( bare ( tallliste ) ) :
resultatområde. legge til ( tallliste [ k ] )
komme tilbake resultatområde
# Mål tiden det tar å utføre omvendt iterasjon
reverse_time = tt. Timei ( loop_reverse_iteration , Antall = 1000 )
# Mål tiden det tar å utføre rekkeviddeiterasjon
range_time = tt. Timei ( loop_range_iteration , Antall = 1000 )
skrive ut ( 'Nummerliste inneholder 100 000 poster' )
skrive ut ( f 'Omvendt iterasjonstid: {reverse_time:.6f} sekunder' )
skrive ut ( f 'Range Iteration Time: {range_time:.6f} sekunder' )

Unngå unødvendige funksjonsanrop

Det er noen overhead hver gang en funksjon kalles. Koden kjører raskere hvis unødvendige funksjonsanrop unngås. For eksempel, i stedet for gjentatte ganger å utføre en funksjon som beregner en verdi, prøv å lagre resultatet av beregningen i en variabel og bruke den.

Verktøy for profilering

For å lære mer om ytelsen til koden din, i tillegg til innebygd profilering, kan vi bruke de eksterne profileringspakkene som cProfile, Pyflame eller SnakeViz.

Cache-resultater

Hvis koden vår trenger å utføre dyre beregninger, kan vi vurdere å bufre resultatene for å spare tid.

Kode Refaktorering

Refaktorering av koden for å gjøre den enklere å lese og vedlikeholde er noen ganger en nødvendig del av å optimalisere den. Et raskere program kan også være renere.

Bruk Just-in-Time Compilation (JIT)

Biblioteker som PyPy eller Numba kan tilby en JIT-samling som kan øke hastigheten på visse typer Python-kode betydelig.

Oppgrader Python

Sørg for at du bruker den nyeste versjonen av Python siden nyere versjoner ofte inkluderer ytelsesforbedringer.

Parallellisme og samtidighet

For prosesser som kan parallelliseres, undersøk parallell- og synkroniseringsteknikkene som multiprosessering, tråding eller asyncio.

Husk at benchmarking og profilering bør være hoveddriverne for optimalisering. Konsentrer deg om å forbedre de områdene i koden vår som har de mest betydelige effektene på ytelsen, og test stadig forbedringene dine for å sikre at de har de ønskede effektene uten å introdusere flere defekter.

Konklusjon

Avslutningsvis er Python-kodeoptimalisering avgjørende for forbedret ytelse og ressurseffektivitet. Utviklere kan øke utførelseshastigheten og responsen til Python-applikasjonene sine betraktelig ved å bruke ulike teknikker som å velge riktige datastrukturer, utnytte de innebygde funksjonene, redusere de ekstra løkkene og effektivt administrere minnet. Kontinuerlig benchmarking og profilering bør styre optimaliseringsinnsatsen, og sikre at kodeutviklingen samsvarer med de virkelige ytelseskravene. For å garantere en langsiktig prosjektsuksess og redusere sjansen for å introdusere nye problemer, bør optimalisering av koden hele tiden balanseres med målene om kodelesbarhet og vedlikeholdbarhet.