Автор оригинала: Chris.
Python поставляется с разными профилировщиками. Если вы новичок в оптимизации производительности, вы можете спросить: Что в любом случае профилировщик?
Профилировщик производительности позволяет вам более внимательно следить за вашим приложением. Если вы просто запустите сценарий Python в своей оболочке, вы ничего не видите, кроме вывода, производимого вашей программой. Но вы не видите, сколько байтов поглощено вашей программой. Вы не видите, как долго работает каждая функция. Вы не видите структуры данных, которые вызвали большинство накладных расходов памяти.
Без этих вещей вы не можете знать, что такое узкое место вашего приложения. И, как вы уже узнали выше, вы не можете начать оптимизировать свой код. Почему? Потому что иначе вы были соучастны в «преждевременной оптимизации» – одновременно из смертоносных грехов в программировании.
Инструменты профилировщики вставляют специальный код в начале и конец каждой рутины для записи, когда рутина запускается, и когда она выходит. С помощью этой информации профилировщик стремится измерить фактическое время, предпринимаемое рутином на каждом вызове. Этот тип профилировщика также может записывать, какие другие процедуры вызываются из рутины. Затем он может отображать время для всей рутины, а также разбить его во времени, затрачиваемого на локальном уровне и времени, потраченном на каждый звонок на другую рутину.
К счастью, есть много профилировщиков. В оставшейся статье я дам вам обзор наиболее важных профилировщиков в Python и как их использовать. Каждый поставляется со ссылкой на дальнейшее чтение.
Python Cprofile
Самый популярный Python Profiler называется CPROFILE Отказ Вы можете импортировать его как любую другую библиотеку, используя оператор:
import cProfile
Простое утверждение, но, тем не менее, мощный инструмент в вашем панели инструментов.
Давайте напишем сценарий Python, который вы можете профиль. Скажем, вы придумаете этот (очень) Swear Python Script, чтобы найти 100 случайных простых чисел от 2 до 1000, которые вы хотите оптимизировать:
import random def guess(): ''' Returns a random number ''' return random.randint(2, 1000) def is_prime(x): ''' Checks whether x is prime ''' for i in range(x): for j in range(x): if i * j == x: return False return True def find_primes(num): primes = [] for i in range(num): p = guess() while not is_prime(p): p = guess() primes += [p] return primes print(find_primes(100)) ''' [733, 379, 97, 557, 773, 257, 3, 443, 13, 547, 839, 881, 997, 431, 7, 397, 911, 911, 563, 443, 877, 269, 947, 347, 431, 673, 467, 853, 163, 443, 541, 137, 229, 941, 739, 709, 251, 673, 613, 23, 307, 61, 647, 191, 887, 827, 277, 389, 613, 877, 109, 227, 701, 647, 599, 787, 139, 937, 311, 617, 233, 71, 929, 857, 599, 2, 139, 761, 389, 2, 523, 199, 653, 577, 211, 601, 617, 419, 241, 179, 233, 443, 271, 193, 839, 401, 673, 389, 433, 607, 2, 389, 571, 593, 877, 967, 131, 47, 97, 443] '''
Программа медленная (и вы чувствуете, что есть много оптимизаций). Но с чего начать?
Как вы уже узнали, вам нужно знать узкое место вашего сценария. Давайте используем модуль Cprofile, чтобы найти его! Единственное, что вам нужно сделать, это добавить следующие две строки к вашему сценарию:
import cProfile cProfile.run('print(find_primes(100))')
Это действительно так просто. Во-первых, вы пишете свой скрипт. Во-вторых, вы называете cprofile.run ()
Способ проанализировать его производительность. Конечно, вам необходимо заменить команду исполнения с вашим конкретным кодом, который вы хотите проанализировать. Например, если вы хотите проверить функцию F42 ()
, вам нужно ввести cprofile.run ('f42 ()')
Отказ
Вот вывод предыдущего фрагмента кода (еще не паниковать):
[157, 773, 457, 317, 251, 719, 227, 311, 167, 313, 521, 307, 367, 827, 317, 443, 359, 443, 887, 241, 419, 103, 281, 151, 397, 433, 733, 401, 881, 491, 19, 401, 661, 151, 467, 677, 719, 337, 673, 367, 53, 383, 83, 463, 269, 499, 149, 619, 101, 743, 181, 269, 691, 193, 7, 883, 449, 131, 311, 547, 809, 619, 97, 997, 73, 13, 571, 331, 37, 7, 229, 277, 829, 571, 797, 101, 337, 5, 17, 283, 449, 31, 709, 449, 521, 821, 547, 739, 113, 599, 139, 283, 317, 373, 719, 977, 373, 991, 137, 797] 3908 function calls in 1.614 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 1.614 1.614:1( ) 535 1.540 0.003 1.540 0.003 code.py:10(is_prime) 1 0.000 0.000 1.542 1.542 code.py:19(find_primes) 535 0.000 0.000 0.001 0.000 code.py:5(guess) 535 0.000 0.000 0.001 0.000 random.py:174(randrange) 535 0.000 0.000 0.001 0.000 random.py:218(randint) 535 0.000 0.000 0.001 0.000 random.py:224(_randbelow) 21 0.000 0.000 0.000 0.000 rpc.py:154(debug) 3 0.000 0.000 0.072 0.024 rpc.py:217(remotecall) 3 0.000 0.000 0.000 0.000 rpc.py:227(asynccall) 3 0.000 0.000 0.072 0.024 rpc.py:247(asyncreturn) 3 0.000 0.000 0.000 0.000 rpc.py:253(decoderesponse) 3 0.000 0.000 0.072 0.024 rpc.py:291(getresponse) 3 0.000 0.000 0.000 0.000 rpc.py:299(_proxify) 3 0.000 0.000 0.072 0.024 rpc.py:307(_getresponse) 3 0.000 0.000 0.000 0.000 rpc.py:329(newseq) 3 0.000 0.000 0.000 0.000 rpc.py:333(putmessage) 2 0.000 0.000 0.047 0.023 rpc.py:560(__getattr__) 3 0.000 0.000 0.000 0.000 rpc.py:57(dumps) 1 0.000 0.000 0.047 0.047 rpc.py:578(__getmethods) 2 0.000 0.000 0.000 0.000 rpc.py:602(__init__) 2 0.000 0.000 0.026 0.013 rpc.py:607(__call__) 2 0.000 0.000 0.072 0.036 run.py:354(write) 6 0.000 0.000 0.000 0.000 threading.py:1206(current_thread) 3 0.000 0.000 0.000 0.000 threading.py:216(__init__) 3 0.000 0.000 0.072 0.024 threading.py:264(wait) 3 0.000 0.000 0.000 0.000 threading.py:75(RLock) 3 0.000 0.000 0.000 0.000 {built-in method _struct.pack} 3 0.000 0.000 0.000 0.000 {built-in method _thread.allocate_lock} 6 0.000 0.000 0.000 0.000 {built-in method _thread.get_ident} 1 0.000 0.000 1.614 1.614 {built-in method builtins.exec} 6 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance} 9 0.000 0.000 0.000 0.000 {built-in method builtins.len} 1 0.000 0.000 0.072 0.072 {built-in method builtins.print} 3 0.000 0.000 0.000 0.000 {built-in method select.select} 3 0.000 0.000 0.000 0.000 {method '_acquire_restore' of '_thread.RLock' objects} 3 0.000 0.000 0.000 0.000 {method '_is_owned' of '_thread.RLock' objects} 3 0.000 0.000 0.000 0.000 {method '_release_save' of '_thread.RLock' objects} 3 0.000 0.000 0.000 0.000 {method 'acquire' of '_thread.RLock' objects} 6 0.071 0.012 0.071 0.012 {method 'acquire' of '_thread.lock' objects} 3 0.000 0.000 0.000 0.000 {method 'append' of 'collections.deque' objects} 535 0.000 0.000 0.000 0.000 {method 'bit_length' of 'int' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 3 0.000 0.000 0.000 0.000 {method 'dump' of '_pickle.Pickler' objects} 2 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects} 553 0.000 0.000 0.000 0.000 {method 'getrandbits' of '_random.Random' objects} 3 0.000 0.000 0.000 0.000 {method 'getvalue' of '_io.BytesIO' objects} 3 0.000 0.000 0.000 0.000 {method 'release' of '_thread.RLock' objects} 3 0.000 0.000 0.000 0.000 {method 'send' of '_socket.socket' objects}
Давайте деконструируем его правильно понимать значение вывода. Имя файла вашего сценария – «Code.py». Вот первая часть:
>>>import cProfile >>>cProfile.run('print(find_primes(100))') [157, 773, 457, 317, 251, 719, 227, 311, 167, 313, 521, 307, 367, 827, 317, 443, 359, 443, 887, 241, 419, 103, 281, 151, 397, 433, 733, 401, 881, 491, 19, 401, 661, 151, 467, 677, 719, 337, 673, 367, 53, 383, 83, 463, 269, 499, 149, 619, 101, 743, 181, 269, 691, 193, 7, 883, 449, 131, 311, 547, 809, 619, 97, 997, 73, 13, 571, 331, 37, 7, 229, 277, 829, 571, 797, 101, 337, 5, 17, 283, 449, 31, 709, 449, 521, 821, 547, 739, 113, 599, 139, 283, 317, 373, 719, 977, 373, 991, 137, 797] ...
Он по-прежнему дает вам вывод на оболочку – даже если вы не выполняли код напрямую, cprofile.run ()
Функция сделала. Вы можете увидеть список из 100 случайных простых чисел здесь.
Следующая часть печатает некоторую статистику в оболочку:
3908 function calls in 1.614 seconds
Хорошо, это интересно: вся программа заняла 1,614 секунды, чтобы выполнить. Всего было выполнено 3908 вызовов функций. Можете ли вы понять, какие?
- Функция Print () один раз.
- Функция Find_primes (100) один раз.
- Функция find_primes () выполняет цикл для цикла 100 раз.
- В цикле для цикла мы выполняем диапазон (), Угадайте () и IS_PRIME () функции. Программа выполняет функции угадателя () и IS_PRIME () несколько раз на петлевую итерацию до тех пор, пока она не догадалась не догадаться следующим простым номером.
- Функция Угадания () выполняет метод Randint (21000) один раз.
Следующая часть вывода показывает вам подробную статию имен функций, заказанные именем функции (не его производительность):
Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 1.614 1.614:1( ) 535 1.540 0.003 1.540 0.003 code.py:10(is_prime) 1 0.000 0.000 1.542 1.542 code.py:19(find_primes) ...
Каждая строка обозначает одну функцию. Например, вторая строка обозначает функцию is_prime. Вы можете видеть, что IS_PRIME () имел 535 казней с общим временем 1,54 секунды.
Ух ты! Вы только что нашли узкое место всей программы: is_prime (). Опять же, полное время выполнения составляло 1,614 секунды, и эта функция доминирует на 95% от общего времени выполнения!
Итак, вам нужно задать себе следующие вопросы: нужно ли оптимизировать код вообще? Если вы сделаете, как вы можете смягчить узкое место?
Есть две основные идеи:
- Позвоните в функцию is_prime () реже, а также
- Оптимизировать производительность самой функции.
Вы знаете, что лучший способ оптимизировать код – поиск более эффективных алгоритмов. Быстрый Поиск показывает гораздо более эффективный алгоритм (см. Функцию IS_PRIME2 ()
).
import random def guess(): ''' Returns a random number ''' return random.randint(2, 1000) def is_prime(x): ''' Checks whether x is prime ''' for i in range(x): for j in range(x): if i * j == x: return False return True def is_prime2(x): ''' Checks whether x is prime ''' for i in range(2,int(x**0.5)+1): if x % i == 0: return False return True def find_primes(num): primes = [] for i in range(num): p = guess() while not is_prime2(p): p = guess() primes += [p] return primes import cProfile cProfile.run('print(find_primes(100))')
Как вы думаете: наша новая главная проверка быстрее? Давайте изучим результат нашего фрагмента кода:
[887, 347, 397, 743, 751, 19, 337, 983, 269, 547, 823, 239, 97, 137, 563, 757, 941, 331, 449, 883, 107, 271, 709, 337, 439, 443, 383, 563, 127, 541, 227, 929, 127, 173, 383, 23, 859, 593, 19, 647, 487, 827, 311, 101, 113, 139, 643, 829, 359, 983, 59, 23, 463, 787, 653, 257, 797, 53, 421, 37, 659, 857, 769, 331, 197, 443, 439, 467, 223, 769, 313, 431, 179, 157, 523, 733, 641, 61, 797, 691, 41, 751, 37, 569, 751, 613, 839, 821, 193, 557, 457, 563, 881, 337, 421, 461, 461, 691, 839, 599] 4428 function calls in 0.074 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.073 0.073:1( ) 610 0.002 0.000 0.002 0.000 code.py:19(is_prime2) 1 0.001 0.001 0.007 0.007 code.py:27(find_primes) 610 0.001 0.000 0.004 0.000 code.py:5(guess) 610 0.001 0.000 0.003 0.000 random.py:174(randrange) 610 0.001 0.000 0.004 0.000 random.py:218(randint) 610 0.001 0.000 0.001 0.000 random.py:224(_randbelow) 21 0.000 0.000 0.000 0.000 rpc.py:154(debug) 3 0.000 0.000 0.066 0.022 rpc.py:217(remotecall)
Сумасшедший – какое улучшение производительности! С старом узким местом код занимает 1,6 секунды. Теперь это занимает всего 0,074 секунды – улучшение производительности времени выполнения 95%!
Это сила анализа узкой узки.
Способ CPROFILE имеет много дополнительных функций и параметров, но этот простой метод CPROFILE.RUN () уже достаточно, чтобы разрешить много производительности узких мест. Если вы хотите узнать больше, изучите официальную документацию.
Куда пойти отсюда?
Вы узнали, как использовать модуль Cprofile в Python, чтобы найти узкое место вашего приложения.
Если вы уже оптимизируете производительность своих приложений Python, шансы состоят в том, что вы уже можете заработать шесть фигур, продавая свои навыки Python. Вы хотели бы узнать, как?
Присоединяйтесь к свободному вебинару, которое показывает, как стать процветающим владельцем бизнеса в Интернете!
[Вебинар] Вы являетесь личным разработчиком Freelance Six
Присоединяйтесь к нам. Это весело! 🙂
Работая в качестве исследователя в распределенных системах, доктор Кристиан Майер нашел свою любовь к учению студентов компьютерных наук.
Чтобы помочь студентам достичь более высоких уровней успеха Python, он основал сайт программирования образования Finxter.com Отказ Он автор популярной книги программирования Python одноклассники (Nostarch 2020), Coauthor of Кофе-брейк Python Серия самооставленных книг, энтузиаста компьютерных наук, Фрилансера и владелец одного из лучших 10 крупнейших Питон блоги по всему миру.
Его страсти пишут, чтение и кодирование. Но его величайшая страсть состоит в том, чтобы служить стремлению кодер через Finxter и помогать им повысить свои навыки. Вы можете присоединиться к его бесплатной академии электронной почты здесь.
Оригинал: “https://blog.finxter.com/python-cprofile-a-helpful-guide-with-prime-example/”