Kaip optimizuoti Python scenarijus, kad jie veiktų geriau

Kaip Optimizuoti Python Scenarijus Kad Jie Veiktu Geriau



Optimizuojant Python scenarijus siekiant geresnio našumo, reikia nustatyti ir pašalinti mūsų kodo kliūtis, kad jis veiktų greičiau ir efektyviau. Python yra populiari ir galinga programavimo kalba, kuri šiais laikais naudojama daugelyje programų, įskaitant duomenų analizę, ML projektus (mašininį mokymąsi), interneto svetainių kūrimą ir daug daugiau. Python kodo optimizavimas yra strategija, skirta pagerinti kūrėjo programos greitį ir efektyvumą atliekant bet kokią veiklą naudojant mažiau kodo eilučių, mažiau atminties ar papildomų išteklių. Didelis ir neefektyvus kodas gali sulėtinti programos veikimą, o tai gali lemti prastą klientų pasitenkinimą ir galimus finansinius nuostolius, arba gali prireikti daugiau darbo taisant ir šalinant triktis.

Tai būtina atliekant užduotį, kuri reikalauja apdoroti kelis veiksmus ar duomenis. Todėl kai kurių neveiksmingų kodų blokų ir funkcijų išjungimas ir patobulinimas gali turėti nuostabių rezultatų, pavyzdžiui:

  1. Padidinkite programos našumą
  2. Sukurkite skaitomą ir sutvarkytą kodą
  3. Padarykite klaidų stebėjimą ir derinimą paprastesnį
  4. Išsaugokite didelę skaičiavimo galią ir pan

Profiliuokite savo kodą

Prieš pradedant optimizuoti, būtina nustatyti projekto kodo dalis, kurios jį lėtina. „Python“ profiliavimo metodai apima „cProfile“ ir profilių paketus. Naudokite tokius įrankius, kad įvertintumėte, kaip greitai vykdomos tam tikros funkcijos ir kodo eilutės. Modulis cProfile sukuria ataskaitą, kurioje išsamiai aprašoma, kiek laiko užtrunka kiekviena scenarijaus funkcija. Ši ataskaita gali padėti mums rasti visas lėtai veikiančias funkcijas, kad galėtume jas patobulinti.







Kodo fragmentas:



importuoti cProfilis kaip cP
def apskaičiuotiSuma ( inputNumber ) :
įvesties_skaičių_suma = 0
kol inputNumber > 0 :
įvesties_skaičių_suma + = inputNumber % 10
inputNumber // = 10
spausdinti ( 'Visų įvesties numerio skaitmenų suma yra: 'įvesties_numerių_suma'' )
grąžinti įvesties_skaičių_suma
def main_func ( ) :
cP. paleisti ( 'calculateSum(9876543789)' )
jeigu __vardas__ == '__pagrindinis__' :
main_func ( )

Programa iš viso atlieka penkis funkcijų iškvietimus, kaip matyti pirmoje išvesties eilutėje. Išsami informacija apie kiekvieną funkcijos iškvietimą rodoma šiose keliose eilutėse, įskaitant skaičių, kiek kartų funkcija buvo iškviesta, bendrą funkcijos trukmę, skambučio trukmę ir bendrą funkcijos trukmę (įskaitant visos jos vadinamos funkcijos).



Be to, programa išspausdina ataskaitą raginimo ekrane, kurioje rodoma, kad programa užbaigia visų savo užduočių vykdymo laiką per 0 000 sekundžių. Tai parodo, kaip greitai veikia programa.





Pasirinkite tinkamą duomenų struktūrą

Veikimo charakteristikos priklauso nuo duomenų struktūros. Visų pirma, žodynai yra greitesni, nei sąrašai, susiję su bendrosios paskirties saugykla. Pasirinkite duomenų struktūrą, kuri labiausiai tinka operacijoms, kurias atliksime su jūsų duomenimis, jei jas žinote. Šiame pavyzdyje nagrinėjamas skirtingų duomenų struktūrų efektyvumas identiškam procesui, siekiant nustatyti, ar duomenų struktūroje yra elementas.



Įvertiname laiką, kurio reikia norint patikrinti, ar elementas yra kiekvienoje duomenų struktūroje – sąraše, rinkinyje ir žodyne – ir juos palyginame.

OptimizeDataType.py:

importuoti Timei kaip tt
importuoti atsitiktinis kaip rndobj
# Sukurkite sveikųjų skaičių sąrašą
random_data_list = [ rndobj. randint ( 1 , 10 000 ) dėl _ in diapazonas ( 10 000 ) ]
# Sukurkite rinkinį iš tų pačių duomenų
random_data_set = rinkinys ( random_data_list )

# Sukurkite žodyną su tais pačiais duomenimis kaip ir raktai
obj_DataDictionary = { ant vieno: Nė vienas dėl ant vieno in random_data_list }

# Elementas, kurio reikia ieškoti (duomenyse yra)
atsitiktinis_skaičius_rasti = rndobj. pasirinkimas ( random_data_list )

# Išmatuokite laiką, kad patikrintumėte narystę sąraše
list_time = tt. Timei ( lambda : atsitiktinis_skaičius_rasti in random_data_list , numerį = 1000 )

# Išmatuokite laiką, kad patikrintumėte narystę rinkinyje
nustatyti laiką = tt. Timei ( lambda : atsitiktinis_skaičius_rasti in random_data_set , numerį = 1000 )

# Išmatuokite laiką, kad patikrintumėte narystę žodyne
dict_time = tt. Timei ( lambda : atsitiktinis_skaičius_rasti in obj_DataDictionary , numerį = 1000 )

spausdinti ( f „Sąrašo narystės tikrinimo laikas: {list_time:.6f} sek.“ )
spausdinti ( f „Nustatyti narystės tikrinimo laiką: {set_time:.6f} sek.“ )
spausdinti ( f „Žodyno narystės tikrinimo laikas: {dict_time:.6f} sek.“ )

Šis kodas palygina sąrašų, rinkinių ir žodynų našumą atliekant narystės patikras. Apskritai rinkiniai ir žodynai yra daug greitesni nei narystės testų sąrašai, nes juose naudojamos maišos pagrindu sukurtos peržvalgos, todėl jų vidutinis laiko sudėtingumas yra O(1). Kita vertus, sąrašuose turi būti atliekamos tiesinės paieškos, dėl kurių atliekami narystės testai su O (n) laiko sudėtingumu.

  Automatiškai sugeneruota kompiuterio ekrano kopija Aprašymas

Vietoj kilpų naudokite integruotas funkcijas

Daugybė integruotų Python funkcijų arba metodų gali būti naudojami atliekant tipines užduotis, pvz., filtravimą, rūšiavimą ir atvaizdavimą. Šių veiksmų naudojimas, o ne kilpų kūrimas, padeda pagreitinti kodą, nes jie dažnai optimizuojami pagal našumą.

Sukurkime kodo pavyzdį, kad palygintume tinkintų kilpų kūrimo našumą naudodami įprastoms užduotims įtaisytas funkcijas (pvz., map(), filter() ir sorted()). Įvertinsime, kaip veikia įvairūs kartografavimo, filtravimo ir rūšiavimo metodai.

BuiltInFunctions.py:

importuoti Timei kaip tt
# Pavyzdinis numerių_sąrašo sąrašas
numerių_sąrašas = sąrašą ( diapazonas ( 1 , 10 000 ) )

# Funkcija kvadratiniam numerių sąrašui naudojant kilpą
def square_using_loop ( numerių_sąrašas ) :
kvadratinis_rezultatas = [ ]
dėl ant vieno in numbers_list:
kvadratinis_rezultatas. pridėti ( ant vieno ** 2 )
grąžinti kvadratinis_rezultatas
# Funkcija, skirta filtruoti lyginių skaičių_sąrašą naudojant kilpą
def filtras_even_using_loop ( numerių_sąrašas ) :
filtras_rezultatas = [ ]
dėl ant vieno in numbers_list:
jeigu ant vieno % 2 == 0 :
filtras_rezultatas. pridėti ( ant vieno )
grąžinti filtras_rezultatas
# Funkcija rūšiuoti numbers_list naudojant kilpą
def Rūšiuoti_naudojant_ciklą ( numerių_sąrašas ) :
grąžinti surūšiuoti ( numerių_sąrašas )
# Išmatuokite laiką iki kvadratinių skaičių_sąrašo naudodami map()
map_time = tt. Timei ( lambda : sąrašą ( žemėlapį ( lambda x: x ** 2 , numerių_sąrašas ) ) , numerį = 1000 )
# Išmatuokite porinių skaičių_sąrašo filtravimo laiką naudodami filtrą ()
filtro_laikas = tt. Timei ( lambda : sąrašą ( filtras ( lambda x: x % 2 == 0 , numerių_sąrašas ) ) , numerį = 1000 )
# Išmatuokite skaičių_sąrašo rūšiavimo laiką naudodami sorted()
surūšiuotas_laikas = tt. Timei ( lambda : surūšiuoti ( numerių_sąrašas ) , numerį = 1000 )
# Išmatuokite skaičių kvadrato skaičių_sąrašo sudarymo laiką naudodami kilpą
loop_map_time = tt. Timei ( lambda : square_using_loop ( numerių_sąrašas ) , numerį = 1000 )
# Išmatuokite lyginių skaičių_sąrašo filtravimo laiką naudodami kilpą
kilpos_filtro_laikas = tt. Timei ( lambda : filtras_even_using_loop ( numerių_sąrašas ) , numerį = 1000 )
# Išmatuokite skaičių_sąrašo rūšiavimo laiką naudodami kilpą
loop_sorted_time = tt. Timei ( lambda : sort_using_loop ( numerių_sąrašas ) , numerį = 1000 )
spausdinti ( 'Skaičių sąraše yra 10 000 elementų' )
spausdinti ( f 'Map() Time: {map_time:.6f} seconds' )
spausdinti ( f 'Filter() Time: {filter_time:.6f} seconds' )
spausdinti ( f „Rūšiavimo() laikas: {rūšiavimo laikas:.6f} sek.“ )
spausdinti ( f „Cilpos (žemėlapio) laikas: {loop_map_time:.6f} sek.“ )
spausdinti ( f „Cilpos (filtro) laikas: {loop_filter_time:.6f} sek.“ )
spausdinti ( f „Cilpos (rūšiavimo) laikas: {loop_sorted_time:.6f} sek.“ )

Tikėtina, kad pastebėsime, kad integruotos funkcijos (žemėlapis (), filtras () ir rūšiavimas ()) yra greitesnės nei šių bendrų užduočių pasirinktinės kilpos. Integruotos „Python“ funkcijos siūlo glaustesnį ir suprantamesnį požiūrį į šias užduotis ir yra labai optimizuotos našumui.

Optimizuokite kilpas

Jei kilpų rašymas yra būtinas, yra keletas būdų, kaip juos paspartinti. Paprastai diapazono() ciklas yra greitesnis nei kartojimas atgal. Taip yra todėl, kad diapazonas() generuoja iteratorių neapversdamas sąrašo, o tai gali būti brangi operacija ilgiems sąrašams. Be to, kadangi diapazonas () nesukuria naujo sąrašo atmintyje, jis naudoja mažiau atminties.

OptimizeLoop.py:

importuoti Timei kaip tt
# Pavyzdinis numerių_sąrašo sąrašas
numerių_sąrašas = sąrašą ( diapazonas ( 1 , 100 000 ) )
# Funkcija kartoti sąrašą atvirkštine tvarka
def loop_reverse_iteration ( ) :
rezultatas_atvirkštinis = [ ]
dėl j in diapazonas ( tik ( numerių_sąrašas ) - 1 , - 1 , - 1 ) :
rezultatas_atvirkštinis. pridėti ( numerių_sąrašas [ j ] )
grąžinti rezultatas_atvirkštinis
# Funkcija kartoti sąrašą naudojant range ()
def kilpos_diapazono_iteracija ( ) :
rezultatų_diapazonas = [ ]
dėl k in diapazonas ( tik ( numerių_sąrašas ) ) :
rezultatų_diapazonas. pridėti ( numerių_sąrašas [ k ] )
grąžinti rezultatų_diapazonas
# Išmatuokite laiką, kurio reikia atvirkštinei iteracijai atlikti
atvirkštinis_laikas = tt. Timei ( loop_reverse_iteration , numerį = 1000 )
# Išmatuokite laiką, kurio reikia diapazono iteracijai atlikti
diapazono_laikas = tt. Timei ( kilpos_diapazono_iteracija , numerį = 1000 )
spausdinti ( 'Skaičių sąraše yra 100 000 įrašų' )
spausdinti ( f „Atvirkštinės iteracijos laikas: {reverse_time:.6f} sek.“ )
spausdinti ( f „Range Iteration Time: {range_time:.6f} seconds“ )

Venkite nereikalingų funkcijų skambučių

Kiekvieną kartą, kai iškviečiama funkcija, atsiranda papildomų išlaidų. Kodas veikia greičiau, jei išvengiama nereikalingų funkcijų iškvietimų. Pavyzdžiui, užuot pakartotinai vykdydami funkciją, kuri apskaičiuoja reikšmę, pabandykite išsaugoti skaičiavimo rezultatą kintamajame ir naudoti jį.

Profiliavimo įrankiai

Norėdami sužinoti daugiau apie kodo veikimą, be integruoto profiliavimo, galime naudoti išorinius profiliavimo paketus, tokius kaip cProfile, Pyflame arba SnakeViz.

Talpyklos rezultatai

Jei mūsų kodui reikia atlikti brangius skaičiavimus, galime apsvarstyti galimybę išsaugoti rezultatus talpykloje, kad sutaupytume laiko.

Kodo pertvarkymas

Kodo pertvarkymas, kad jį būtų lengviau skaityti ir prižiūrėti, kartais yra būtina jo optimizavimo dalis. Greitesnė programa taip pat gali būti švaresnė.

Naudokite „Just-in-Time“ rinkinį (JIT)

Tokios bibliotekos kaip PyPy ar Numba gali pateikti JIT kompiliaciją, kuri gali žymiai pagreitinti tam tikrų tipų Python kodą.

Atnaujinkite Python

Įsitikinkite, kad naudojate naujausią Python versiją, nes naujesnėse versijose dažnai yra našumo patobulinimų.

Lygiagretumas ir sutapimas

Jei procesai gali būti lygiagretinami, ištirkite lygiagrečius ir sinchronizavimo būdus, pvz., kelių apdorojimo, gijų arba asinchronizavimo metodus.

Atminkite, kad lyginamoji analizė ir profiliavimas turėtų būti pagrindiniai optimizavimo veiksniai. Susikoncentruokite į mūsų kodo sričių, kurios turi didžiausią poveikį našumui, tobulinimą ir nuolat tikrinkite savo patobulinimus, kad įsitikintumėte, jog jie turi pageidaujamą poveikį, nesukeldami daugiau defektų.

Išvada

Apibendrinant galima pasakyti, kad „Python“ kodo optimizavimas yra labai svarbus siekiant pagerinti našumą ir išteklių efektyvumą. Kūrėjai gali žymiai padidinti savo Python programų vykdymo greitį ir reagavimą naudodami įvairius metodus, tokius kaip tinkamų duomenų struktūrų parinkimas, integruotų funkcijų panaudojimas, papildomų kilpų mažinimas ir efektyvus atminties valdymas. Nuolatinis lyginamoji analizė ir profiliavimas turėtų nukreipti optimizavimo pastangas, užtikrinant, kad kodo patobulinimai atitiktų realaus pasaulio našumo reikalavimus. Siekiant užtikrinti ilgalaikę projekto sėkmę ir sumažinti naujų problemų atsiradimo tikimybę, kodo optimizavimas turėtų būti nuolat derinamas su kodo skaitomumo ir priežiūros tikslais.