Как да използвам cProfile за профилиране на Python код

Python може да не е най-бързият език наоколо, но често е достатъчно бърз. А Python е идеален, когато времето на програмиста е по-важно от времето на процесора.

Въпреки това, ако дадено приложение на Python изостава, не сте длъжни просто да го изсмуквате. Инструментите, включени в инсталацията на запасите на интерпретатора на Python, могат да ви предоставят подробна обратна връзка за това кои части от вашата програма са бавни и да предложат някои съвети как да ги ускорите.

Как да използвам cProfile

В cProfileмодула събира статистически данни за времето за изпълнение на програма на Python. Той може да докладва за всичко от цялото приложение до един израз или израз.

Ето пример за играчка как да използвате cProfile:

def add (x, y): x + = str (y) return x def add_2 (x, y): if y% 20000 == 0: z = [] за q в диапазон (0,400000): z.append ( q) def main (): a = [] за n в обхват (0,200000): добавете (a, n) add_2 (a, n) ако __name__ == '__main__': импортирайте cProfile cProfile.run ('main ( ) ') 

Този пример изпълнява функцията на приложението main()и анализира производителността на main()и всичко main()извиква. Също така е възможно да се анализира само  част от програма, но най-често използваната за начало е да се профилира цялата програма.

Изпълнете горния пример и ще бъдете посрещнати с нещо като следния изход:

Това, което е показано тук, е списък на всички извиквания на функции, извършени от програмата, заедно със статистически данни за всеки:

  • В горната част (първи ред в синьо) виждаме общия брой обаждания, направени в профилираната програма, и общото време за изпълнение. Може да видите и фигура за „примитивни повиквания“, което означава нерекурсивни повиквания или обаждания, направени директно към функция, която на свой ред не се извиква по-надолу в стека на повикванията.
  • ncalls : Брой осъществени обаждания. Ако видите две числа, разделени с наклонена черта, второто число е броят на примитивните повиквания за тази функция.
  • tottime : Общо време, прекарано във функцията, без да се включват повиквания към други функции.
  • percall : Средно време на повикване за време за извеждане , получено чрез вземане на време и разделяне на ncalls .
  • cumtime : Общото време, прекарано във функцията, включително повиквания към други функции.
  • percall (# 2): Средно време на разговор за cumtime ( cumtime разделено на ncalls ).
  • filename: lineno : Името на файла, номерът на реда и името на функцията за въпросното повикване.

Как да модифицирам отчетите на cProfile

По подразбиране cProfileсортира изхода си по „стандартно име“, което означава, че той сортира по текста в крайната дясна колона (име на файл, номер на ред и т.н.).

Форматът по подразбиране е полезен, ако искате общ отчет отгоре надолу за всяко едно извикване на функция за справка. Но ако се опитвате да стигнете до дъното на пречка, вероятно ще искате най-трудоемките части от програмата, изброени първо.

Можем да произведем тези резултати, като се позоваваме  cProfile малко по-различно. Обърнете внимание как долната част на горната програма може да бъде преработена, за да сортира статистиката по различна колона (в този случай ncalls):

ако __name__ == '__main__': импортиране на cProfile, pstats profiler = cProfile.Profile () profiler.enable () main () profiler.disable () stats = pstats.Stats (profiler) .sort_stats ('ncalls') stats.print_stats () 

Резултатите ще изглеждат по следния начин:

Ето как работи всичко това:

  • Вместо да изпълнява команда чрез cProfile.run(), което не е много гъвкава, създаваме профилиране обект , profiler.
  • Когато искаме да профилираме някакво действие, първо извикваме .enable()екземпляра на обекта на профилиращия, след това изпълняваме действието, след което извикваме .disable(). (Това е един от начините да профилирате само част от програма.)
  • В pstatsмодула се използва за манипулиране на данните, събрани от обекта профайлър резултатите и да отпечатате тези резултати.

Комбинирането на обект на профилиране и pstatsни позволява да манипулираме заснетите данни на профила - например да сортираме генерираната статистика по различен начин. В този пример използването на .sort_stats('ncalls')сортира статистическите данни по ncallsколоната. Предлагат се и други опции за сортиране.

Как да използвам резултатите от cProfile за оптимизация

Опциите за сортиране, които са достъпни за cProfile изход, ни позволяват да раздразним потенциалните тесни места в изпълнението на програмата.

ncalls

Първата и най-значима информация, която можете да откриете, cProfileе кои функции се извикват най-често чрез ncallsколоната.

В Python самото извършване на извикване на функция води до относително голямо количество режийни разходи. Ако някоя функция се извиква многократно в стегнат цикъл, дори ако не е дълготрайна функция, това гарантирано ще повлияе на производителността.

В горния пример функцията add(и функцията add_2) се извиква многократно в цикъл. Преместването на цикъла в самата addфункция или вграждането на addфункцията изцяло би решило този проблем.

tottime

Друга полезна статистическа информация, която функционира, като програмата прекарва по-голямата част от времето си в tottimeколона.

В горния пример add_2функцията използва цикъл, за да симулира някакво скъпо изчисление, което изтласква tottimeрезултата му на върха. Всяка функция с висока tottimeоценка заслужава внимателен поглед, особено ако е извикана многократно или в стегнат цикъл.

Имайте предвид, че винаги трябва да имате предвид контекста,  в който се използва функцията. Ако дадена функция има висок, tottimeно се извиква само веднъж - например само когато програмата стартира - по-малко вероятно е тясно място. Ако обаче се опитвате да намалите времето за стартиране, ще искате да знаете дали функция, извикана при стартиране, кара всичко останало да чака.

Как да експортирам cProfile данни

Ако искате да използвате cProfileгенерираната статистика по по-разширени начини, можете да ги експортирате във файл с данни:

stats = pstats.Stats (профилиране) stats.dump_stats ('/ path / to / stats_file.dat') 

Този файл може да бъде прочетен обратно с помощта на pstatsмодула, след това сортиран или показан с pstats. Данните могат да се използват повторно и от други програми. Два примера:

  • pyprof2calltreeправи подробни визуализации на графиката на повикванията на програмата и статистиката за използване от данните на профила. Тази статия предоставя подробен пример за неговото използване в реалния свят.
  • snakevizсъщо така генерира визуализации от cProfileданни, но използва различно представяне за данните - „слънчев изблик“, а не графика „пламък“ на pyprof2calltree

Отвъд cProfile за профилиране на Python

cProfileедва ли е единственият начин за профилиране на приложение на Python. cProfileсъс сигурност е един от най-удобните начини, като се има предвид, че е в комплект с Python. Но други заслужават внимание.

Един проект, py-spyизгражда профил за приложение на Python чрез вземане на проби от неговата активност на повикване. py-spyможе да се използва за изследване на работещо приложение на Python, без да се налага да го спирате и рестартирате и без да се налага да променяте кодовата му база, така че да може да се използва за профилиране на внедрени приложения. py-spyсъщо така генерира някои статистически данни за режийните разходи, възникнали от времето на изпълнение на Python (например режийни разходи за събиране на боклук), което cProfileне е така.