A Python-kód profilozása timeit és cProfile használatával - KDnuggets

A Python-kód profilozása timeit és cProfile használatával – KDnuggets

Forrás csomópont: 2874649

A Python-kód profilozása timeit és cProfile használatával
A kép szerzője
 

Szoftverfejlesztőként valószínűleg hallotta az idézetet "Az idő előtti optimalizálás minden rossz gyökere"– többször – a karrierje során. Bár az optimalizálás nem feltétlenül hasznos (vagy nem feltétlenül szükséges) kis projekteknél, a profilalkotás gyakran hasznos. 

Miután befejezte a modul kódolását, jó gyakorlat a kód profilozása, hogy mérje, mennyi ideig tart az egyes szakaszok végrehajtása. Ez segíthet azonosítani a kódszagokat, és optimalizálni tudja a kód minőségét. Tehát az optimalizálás előtt mindig profilozza meg kódját!

Az első lépések megtételéhez ez az útmutató segít elkezdeni a profilalkotást Pythonban – a beépített funkció használatával timeit és a cProfile modulok. Megtanulja használni a parancssori felületet és a Python-szkriptekben az azzal egyenértékű hívható elemeket.

A timeit modul a Python szabványkönyvtár része, és kínál néhány kényelmi funkciót, amelyek segítségével rövid kódrészleteket lehet időzíteni.

Vegyünk egy egyszerű példát a Python-lista megfordítására. A lista fordított másolatának beszerzésének végrehajtási idejét a következő módszerrel mérjük:

  • a reversed() funkció, és
  • listaszeletelés. 
>>> nums=[6,9,2,3,7]
>>> list(reversed(nums))
[7, 3, 2, 9, 6]
>>> nums[::-1]
[7, 3, 2, 9, 6]

 

Futási idő a parancssorban

Futtathatod timeit a parancssorban a szintaxis használatával:

$ python -m timeit -s 'setup-code' -n 'number' -r 'repeat' 'stmt'

 

A nyilatkozatot be kell nyújtania stmt amelynek végrehajtási idejét mérni kell. 

Megadhatja a setup kódot, ha szükséges – a rövid -s vagy a long opció -setup használatával. A beállítási kód csak egyszer fut le.

A number hányszor futtassa le az utasítást: a short opció -n vagy a long option -number opcionális. És a ciklus megismétlésének száma: a rövid -r opció vagy a hosszú -repeat opció szintén nem kötelező.

Lássuk a fentieket működés közben a példánkban:

Itt a lista létrehozása a setup kódot, és a lista megfordítása az időzítendő utasítás:

$ python -m timeit -s 'nums=[6,9,2,3,7]' 'list(reversed(nums))'
500000 loops, best of 5: 695 nsec per loop

 

Ha nem ad meg értékeket a repeat, az alapértelmezett 5-ös érték kerül felhasználásra. És amikor nem adja meg number, a kód annyiszor fut le, ahányszor szükséges, hogy elérje a teljes időtartamot legalább 0.2 másodpercig.

Ez a példa kifejezetten beállítja az utasítás végrehajtásának számát:

$ python -m timeit -s 'nums=[6,9,2,3,7]' -n 100Bu000 'list(reversed(nums))'
100000 loops, best of 5: 540 nsec per loop

 

Az alapértelmezett értéke repeat 5, de bármilyen megfelelő értékre állíthatjuk:

$ python3 -m timeit -s 'nums=[6,9,2,3,7]' -r 3 'list(reversed(nums))'
500000 loops, best of 3: 663 nsec per loop

 

Időzítsük a listaszeletelési megközelítést is:

$ python3 -m timeit -s 'nums=[6,9,2,3,7]' 'nums[::-1]'
1000000 loops, best of 5: 142 nsec per loop

 

A listaszeletelési megközelítés gyorsabbnak tűnik (minden példa a Python 3.10-ben található Ubuntu 22.04-en).

A timeit futtatása Python-szkriptben

Íme a Python szkripten belüli timeit futtatásának megfelelője:

import timeit setup = 'nums=[9,2,3,7,6]'
number = 100000
stmt1 = 'list(reversed(nums))'
stmt2 = 'nums[::-1]' t1 = timeit.timeit(setup=setup,stmt=stmt1,number=number)
t2 = timeit.timeit(setup=setup,stmt=stmt2,number=number) print(f"Using reversed() fn.: {t1}")
print(f"Using list slicing: {t2}")

 

A timeit() A callable a végrehajtási idejét adja vissza stmt mert number az időkből. Figyeljük meg, hogy kifejezetten megemlíthetjük, hányszor kell futtatni vagy elkészíteni number vegye az alapértelmezett 1000000 értéket.

Output >>
Using reversed() fn.: 0.08982690000000002
Using list slicing: 0.015550800000000004

 

Ez futtatja az utasítást – az időzítő funkció megismétlése nélkül – a megadott értékre number alkalommal, és visszaadja a végrehajtási időt. Használata is elég gyakori time.repeat() és vegye ki a minimális időt az ábrán látható módon:

import timeit setup = 'nums=[9,2,3,7,6]'
number = 100000
stmt1 = 'list(reversed(nums))'
stmt2 = 'nums[::-1]' t1 = min(timeit.repeat(setup=setup,stmt=stmt1,number=number))
t2 = min(timeit.repeat(setup=setup,stmt=stmt2,number=number)) print(f"Using reversed() fn.: {t1}")
print(f"Using list slicing: {t2}")

 

Ez megismétli a kód futtatásának folyamatát number időkből repeat számú alkalommal, és visszaadja a minimális végrehajtási időt. Itt 5 ismétlés van, mindegyik 100000 XNUMX-szer.

Output >>
Using reversed() fn.: 0.055375300000000016
Using list slicing: 0.015101400000000043

Láttuk, hogy a timeit segítségével mérhető a rövid kódrészletek végrehajtási ideje. A gyakorlatban azonban hasznosabb egy teljes Python-szkript profilozása. 

Ez megadja nekünk az összes függvény és metódushívás végrehajtási idejét – beleértve a beépített függvényeket és metódusokat is. Így jobb képet kaphatunk a drágább függvényhívásokról, és azonosíthatjuk az optimalizálás lehetőségeit. Például: lehet, hogy túl lassú API-hívás. Vagy egy függvénynek lehet egy ciklusa, amely helyettesíthető egy pitonikusabb megértési kifejezéssel. 

Tanuljuk meg a Python-szkriptek profilozását a cProfile modul segítségével (amely szintén a Python szabványkönyvtár része). 

Tekintsük a következő Python-szkriptet:

# main.py
import time def func(num): for i in range(num): print(i) def another_func(num): time.sleep(num) print(f"Slept for {num} seconds") def useful_func(nums, target): if target in nums: return nums.index(target) if __name__ == "__main__": func(1000) another_func(20) useful_func([2, 8, 12, 4], 12)

 

Itt három funkciónk van:

  • func() amely egy számtartományon áthalad és kiírja azokat.
  • another func() amely hívást tartalmaz a sleep() funkciót.
  • useful_func() amely a listában szereplő célszám indexét adja vissza (ha a cél szerepel a listában).

A fent felsorolt ​​függvények a main.py parancsfájl minden egyes futtatásakor meghívódnak.

A cProfile futtatása a parancssorban

Futtassa a cProfile-t a parancssorban a következő használatával:

python3 -m file-name.py

 

Itt neveztük el a fájlt main.py-nek:

python3 -m main.py

 

Ennek futtatásával a következő kimenetet kell kapnia:

 Output >> 0 ... 999 Slept for 20 seconds

 

És a következő profil:

 

A Python-kód profilozása timeit és cProfile használatával
 

Itt, ncalls az és a funkció hívásainak számát jelenti percall a függvényhívásonkénti időre vonatkozik. Ha az értéke ncalls akkor nagyobb, mint egy percall az összes hívás átlagos ideje.

A szkript végrehajtási idejét a another_func amely a beépítettet használja sleep funkcióhívás (20 másodpercig alszik). Ezt látjuk print a függvényhívások is meglehetősen drágák. 

A cProfile használata a Python Scriptben

Miközben a cProfile parancssorban fut, jól működik, a profilalkotási funkciót is hozzáadhatja a Python-szkripthez. Használhatja a cProfile-t a pstats modul profilalkotáshoz és statisztikákhoz való hozzáféréshez.

Az erőforrás-beállítás és a lebontás jobb kezelésének legjobb gyakorlataként használja a with utasítást, és hozzon létre egy profilobjektumot, amelyet környezetkezelőként használnak:

# main.py
import pstats
import time
import cProfile def func(num): for i in range(num): print(i) def another_func(num): time.sleep(num) print(f"Slept for {num} seconds") def useful_func(nums, target): if target in nums: return nums.index(target) if __name__ == "__main__": with cProfile.Profile() as profile: func(1000) another_func(20) useful_func([2, 8, 12, 4], 12) profile_result = pstats.Stats(profile) profile_result.print_stats()

 

Nézzük meg közelebbről a generált kimeneti profilt:

 

A Python-kód profilozása timeit és cProfile használatával
 

Ha nagy forgatókönyvet készít profilba, akkor ez hasznos lesz rendezze az eredményeket végrehajtási idő szerint. Ehhez hívhat sort_stats a profilobjektumra, és rendezze a végrehajtási idő alapján: 

...
if __name__ == "__main__": with cProfile.Profile() as profile: func(1000) another_func(20) useful_func([2, 8, 12, 4], 12) profile_result = pstats.Stats(profile) profile_result.sort_stats(pstats.SortKey.TIME) profile_result.print_stats()

 

Amikor most futtatja a szkriptet, látnia kell az eredményeket idő szerint rendezve:

 

A Python-kód profilozása timeit és cProfile használatával

Remélem, ez az útmutató segít elkezdeni a profilalkotást a Pythonban. Mindig ne feledje, hogy az optimalizálás soha nem mehet az olvashatóság ára. Ha szeretne többet megtudni más profilkészítőkről, beleértve a harmadik féltől származó Python-csomagokat, nézze meg ezt cikk a Python profilozókról.
 
 
Bala Priya C egy indiai fejlesztő és műszaki író. Szeret a matematika, a programozás, az adattudomány és a tartalomkészítés metszéspontjában dolgozni. Érdeklődési területe és szakértelme a DevOps, az adattudomány és a természetes nyelvi feldolgozás. Szeret olvasni, írni, kódolni és kávézni! Jelenleg a tanuláson és tudásának a fejlesztői közösséggel való megosztásán dolgozik oktatóanyagok, útmutatók, véleménycikkek és egyebek készítésével.
 

Időbélyeg:

Még több KDnuggets