Kuidas optimeerida oma Pythoni skripte parema jõudluse saavutamiseks

Kuidas Optimeerida Oma Pythoni Skripte Parema Joudluse Saavutamiseks



Pythoni skriptide optimeerimine parema jõudluse saavutamiseks hõlmab meie koodi kitsaskohtade tuvastamist ja kõrvaldamist, muutes selle kiiremaks ja tõhusamaks. Python on populaarne ja võimas programmeerimiskeel, mida kasutatakse tänapäeval paljudes rakendustes, sealhulgas andmeanalüüsis, ML-projektides (masinõpe), veebiarenduses ja paljudes muudes rakendustes. Pythoni koodi optimeerimine on strateegia arendajaprogrammi kiiruse ja tõhususe parandamiseks, kui sooritate mis tahes toiminguid, kasutades vähem koodiridu, vähem mälu või lisaressursse. Suur ja ebatõhus kood võib programmi tööd aeglustada, mille tulemuseks võib olla klientide halb rahulolu ja võimalik rahaline kahju või vajadus teha rohkem tööd parandamiseks ja tõrkeotsinguks.

See on vajalik mitme toimingu või andmete töötlemist nõudva ülesande täitmisel. Seetõttu võib mõne ebatõhusa koodiploki ja funktsiooni väljalülitamine ja täiustamine anda hämmastavaid tulemusi, näiteks järgmised:

  1. Suurendage rakenduse jõudlust
  2. Looge loetav ja organiseeritud kood
  3. Muutke vigade jälgimine ja silumine lihtsamaks
  4. Säästke palju arvutusvõimsust ja nii edasi

Profiilige oma kood

Enne optimeerimise alustamist on oluline tuvastada projekti koodi need osad, mis seda aeglustavad. Pythonis profiilide koostamise tehnikad hõlmavad cProfile'i ja profiilipakette. Kasutage selliseid tööriistu, et hinnata, kui kiiresti teatud funktsioonid ja koodiread käivituvad. Moodul cProfile koostab aruande, mis kirjeldab iga skriptifunktsiooni käivitamise aega. See aruanne aitab meil leida aeglaselt töötavaid funktsioone, et saaksime neid täiustada.







Koodilõik:



importida cProfiil nagu cP
def arvuta summa ( sisendNumber ) :
sisendarvude_summa = 0
samas sisendNumber > 0 :
sisendarvude_summa + = sisendNumber % 10
inputNumber // = 10
printida ( 'Sisendnumbri kõigi numbrite summa on: 'sisendnumbrite_summa'' )
tagasi sisendarvude_summa
def main_func ( ) :
cP. jooksma ( 'calculateSum(9876543789)' )
kui __nimi__ == '__peamine__' :
main_func ( )

Programm teeb kokku viis funktsioonikutset, nagu on näha väljundi esimesel real. Iga funktsioonikutse üksikasjad on näidatud järgmistel ridadel, sealhulgas funktsiooni käivitamise kordade arv, funktsiooni üldine kestus, kõne aja kestus ja funktsiooni koguaeg (sh kõik funktsioonid, mida seda nimetatakse).



Lisaks prindib programm viipaekraanile aruande, mis näitab, et programm lõpetab kõigi oma ülesannete täitmise 0,000 sekundi jooksul. See näitab, kui kiire programm on.





Valige õige andmestruktuur

Jõudlusnäitajad sõltuvad andmestruktuurist. Eelkõige on sõnastikud otsingute tegemiseks kiiremad kui üldotstarbelist salvestusruumi käsitlevad loendid. Valige andmestruktuur, mis on teie andmetega tehtavate toimingute jaoks kõige sobivam, kui teate neid. Järgmine näide uurib erinevate andmestruktuuride tõhusust identse protsessi jaoks, et teha kindlaks, kas andmestruktuuris on elementi.



Hindame aega, mis kulub elemendi olemasolu kontrollimiseks igas andmestruktuuris – loendis, komplektis ja sõnastikus – ja võrdleme neid.

OptimizeDataType.py:

importida Timei nagu tt
importida juhuslik nagu rndobj
# Looge täisarvude loend
random_data_list = [ rndobj. randint ( 1 , 10 000 ) jaoks _ sisse ulatus ( 10 000 ) ]
# Looge komplekt samadest andmetest
random_data_set = seatud ( random_data_list )

# Loo sõnaraamat samade andmetega nagu võtmed
obj_DataDictionary = { ühel: Mitte ühtegi jaoks ühel sisse random_data_list }

# Element, mida otsida (on andmetes olemas)
juhuslik_leitav_arv = rndobj. valik ( random_data_list )

# Mõõtke aega, et kontrollida nimekirja kuulumist
list_time = tt. Timei ( lambda : juhuslik_arv_leimiseks sisse random_data_list , number = 1000 )

# Mõõtke aega, et kontrollida komplekti kuulumist
määra aeg = tt. Timei ( lambda : juhuslik_arv_leimiseks sisse random_data_set , number = 1000 )

# Mõõtke aega sõnaraamatu liikmelisuse kontrollimiseks
dikt_aeg = tt. Timei ( lambda : juhuslik_arv_leimiseks sisse obj_DataDictionary , number = 1000 )

printida ( f 'Loendi liikmelisuse kontrollimise aeg: {list_time:.6f} sekundit' )
printida ( f 'Määra liikmelisuse kontrolli aeg: {set_time:.6f} sekundit' )
printida ( f 'Sõnastiku liikmelisuse kontrollimise aeg: {dict_time:.6f} sekundit' )

See kood võrdleb liikmelisuse kontrollimisel loendite, komplektide ja sõnaraamatute toimivust. Üldiselt on komplektid ja sõnastikud oluliselt kiiremad kui liikmelisuse testide loendid, kuna need kasutavad räsipõhiseid otsinguid, seega on nende keskmine ajaline keerukus O(1). Teisest küljest peavad loendid tegema lineaarseid otsinguid, mille tulemuseks on O(n) aja keerukusega liikmelisuse testid.

  Ekraanipilt arvutist Kirjeldus genereeritakse automaatselt

Kasutage silmuste asemel sisseehitatud funktsioone

Pythoni sisseehitatud funktsioone või meetodeid saab kasutada tüüpiliste ülesannete, nagu filtreerimine, sortimine ja kaardistamine, täitmiseks. Nende rutiinide kasutamine silmuste loomise asemel aitab koodi kiirendada, kuna need on sageli jõudlusele optimeeritud.

Koostame näidiskoodi, et võrrelda kohandatud silmuste loomise toimivust, kasutades tüüpiliste tööde jaoks sisseehitatud funktsioone (nt map(), filter() ja sorted()). Hindame, kui hästi erinevad kaardistamis-, filtreerimis- ja sortimismeetodid toimivad.

BuiltInFunctions.py:

importida Timei nagu tt
# Numbers_listi näidisloend
numbrite_loend = nimekirja ( ulatus ( 1 , 10 000 ) )

# Funktsioon numbrite_loendi ruudu loomiseks tsükli abil
def square_using_loop ( numbrite_loend ) :
ruut_tulemus = [ ]
jaoks ühel sisse numbers_list:
ruut_tulemus. lisama ( ühel** 2 )
tagasi ruut_tulemus
# Funktsioon paarisarvude_loendi filtreerimiseks tsükli abil
def filter_even_using_loop ( numbrite_loend ) :
filter_result = [ ]
jaoks ühel sisse numbers_list:
kui ühel protsendil 2 == 0 :
filter_result. lisama ( ühel )
tagasi filter_result
# Funktsioon numbers_list sorteerimiseks tsükli abil
def sort_using_loop ( numbrite_loend ) :
tagasi sorteeritud ( numbrite_loend )
# Mõõtke numbrite_loendi ruudu loomise aega, kasutades map()
kaardi_aeg = tt. Timei ( lambda : nimekirja ( kaart ( lambda x: x ** 2 , numbrite_loend ) ) , number = 1000 )
# Mõõtke paarisarvude_loendi filtreerimiseks kuluvat aega, kasutades filter()
filter_time = tt. Timei ( lambda : nimekirja ( filter ( lambda x: x % 2 == 0 , numbrite_loend ) ) , number = 1000 )
# Mõõtke numbers_list sortimiseks kuluvat aega, kasutades sorted()
sorteeritud_aeg = tt. Timei ( lambda : sorteeritud ( numbrite_loend ) , number = 1000 )
# Mõõtke numbrite_loendi ruudu moodustamiseks kuluvat aega tsükli abil
loop_map_time = tt. Timei ( lambda : ruut_kasutav_silmus ( numbrite_loend ) , number = 1000 )
# Mõõtke aega paarisarvude_loendi filtreerimiseks tsükli abil
loop_filter_time = tt. Timei ( lambda : filter_even_using_loop ( numbrite_loend ) , number = 1000 )
# Mõõtke aega numbers_list sorteerimiseks tsükli abil
loop_sorted_time = tt. Timei ( lambda : sort_using_loop ( numbrite_loend ) , number = 1000 )
printida ( 'Numbriloend sisaldab 10 000 elementi' )
printida ( f 'Map() aeg: {map_time:.6f} sekundit' )
printida ( f 'Filter() aeg: {filter_time:.6f} sekundit' )
printida ( f 'Sorteeritud() aeg: {sorted_time:.6f} sekundit' )
printida ( f 'Ringi (kaardi) aeg: {loop_map_time:.6f} sekundit' )
printida ( f 'Tsükli (filtri) aeg: {loop_filter_time:.6f} sekundit' )
printida ( f 'Tsükli (sorditud) aeg: {loop_sorted_time:.6f} sekundit' )

Tõenäoliselt märkame, et sisseehitatud funktsioonid (map(), filter() ja sorted()) on kiiremad kui nende tavaliste ülesannete kohandatud tsüklid. Pythoni sisseehitatud funktsioonid pakuvad nende ülesannete täitmiseks lakoonilisemat ja arusaadavamat lähenemist ning on jõudluse jaoks väga optimeeritud.

Optimeerige silmuseid

Kui silmuste kirjutamine on vajalik, on mõned tehnikad, mida saame nende kiirendamiseks teha. Üldiselt on range() tsükkel kiirem kui tagurpidi itereerimine. Seda seetõttu, et vahemik() genereerib iteraatori ilma loendit ümber pööramata, mis võib pikkade loendite puhul olla kulukas toiming. Lisaks, kuna vahemik() ei loo mällu uut loendit, kasutab see vähem mälu.

OptimizeLoop.py:

importida Timei nagu tt
# Numbers_listi näidisloend
numbrite_loend = nimekirja ( ulatus ( 1 , 100 000 ) )
# Funktsioon loendi kordamiseks vastupidises järjekorras
def loop_reverse_iteration ( ) :
tulemus_tagurpidi = [ ]
jaoks j sisse ulatus ( ainult ( numbrite_loend ) - 1 , - 1 , - 1 ) :
tulemus_tagurpidi. lisama ( numbrite_loend [ j ] )
tagasi tulemus_tagurpidi
# Funktsioon loendi kordamiseks, kasutades vahemikus ()
def loop_range_iteration ( ) :
tulemus_vahemik = [ ]
jaoks k sisse ulatus ( ainult ( numbrite_loend ) ) :
tulemus_vahemik. lisama ( numbrite_loend [ k ] )
tagasi tulemus_vahemik
# Mõõtke vastupidise iteratsiooni sooritamiseks kuluvat aega
vastupidine_aeg = tt. Timei ( loop_reverse_iteration , number = 1000 )
# Mõõtke vahemiku iteratsiooni sooritamiseks kuluvat aega
vahemiku_aeg = tt. Timei ( loop_range_iteration , number = 1000 )
printida ( 'Numbriloend sisaldab 100 000 kirjet' )
printida ( f 'Tagurpidi iteratsiooni aeg: {reverse_time:.6f} sekundit' )
printida ( f Vahemiku iteratsiooni aeg: {range_time:.6f} sekundit )

Vältige tarbetuid funktsioonikõnesid

Iga funktsiooni väljakutsumisega kaasneb lisakulu. Kood töötab kiiremini, kui vältida tarbetuid funktsioonikutseid. Näiteks väärtust arvutava funktsiooni korduva täitmise asemel proovige salvestada arvutuse tulemus muutujasse ja kasutada seda.

Profileerimise tööriistad

Koodi toimivuse kohta lisateabe saamiseks saame lisaks sisseehitatud profileerimisele kasutada väliseid profileerimispakette, nagu cProfile, Pyflame või SnakeViz.

Vahemälu tulemused

Kui meie koodil on vaja teha kalleid arvutusi, võime aja säästmiseks kaaluda tulemuste vahemällu salvestamist.

Koodi ümberfaktoreerimine

Koodi ümberkujundamine, et seda oleks lihtsam lugeda ja hooldada, on mõnikord selle optimeerimise vajalik osa. Kiirem programm võib olla ka puhtam.

Kasutage just-in-time kompilatsiooni (JIT)

Teegid nagu PyPy või Numba võivad pakkuda JIT-kompilatsiooni, mis võib teatud tüüpi Pythoni koodi märkimisväärselt kiirendada.

Täiendage Pythonit

Veenduge, et kasutate Pythoni uusimat versiooni, kuna uuemad versioonid sisaldavad sageli jõudluse täiustusi.

Paralleelsus ja samaaegsus

Paralleelitavate protsesside puhul uurige paralleel- ja sünkroonimistehnikaid, nagu multitöötlus, lõimestamine või asünkroniseerimine.

Pidage meeles, et optimeerimise peamised tõukejõud peaksid olema võrdlusuuringud ja profiilide koostamine. Keskenduge meie koodi nende valdkondade täiustamisele, millel on jõudlusele kõige olulisem mõju, ja testige pidevalt oma täiustusi, veendumaks, et neil on soovitud mõju ilma rohkemate defektideta.

Järeldus

Kokkuvõtteks võib öelda, et Pythoni koodi optimeerimine on jõudluse ja ressursitõhususe parandamiseks ülioluline. Arendajad saavad oma Pythoni rakenduste täitmise kiirust ja reageerimisvõimet märkimisväärselt suurendada, kasutades erinevaid tehnikaid, nagu sobivate andmestruktuuride valimine, sisseehitatud funktsioonide võimendamine, lisasilmuste vähendamine ja mälu tõhus haldamine. Pidev võrdlusuuringud ja profiilide koostamine peaksid suunama optimeerimispüüdlusi, tagades, et koodi täiustused vastavad tegelikele jõudlusnõuetele. Pikaajalise projekti edu tagamiseks ja uute probleemide tekkimise võimaluse vähendamiseks tuleks koodi optimeerimist pidevalt tasakaalustada koodi loetavuse ja hooldatavuse eesmärkidega.