Python LanguageОптимизация производительности

замечания

При попытке улучшить производительность скрипта Python, прежде всего, вы должны найти узкое место вашего сценария и отметить, что никакая оптимизация не может компенсировать плохой выбор структур данных или недостаток в разработке вашего алгоритма. Определение узких мест производительности может быть выполнено путем профилирования вашего скрипта. Во-вторых, не пытайтесь оптимизировать слишком рано в процессе кодирования за счет удобочитаемости / дизайна / качества. Дональд Кнут сделал следующее заявление об оптимизации:

«Мы должны забыть о небольшой эффективности, скажем, около 97% времени: преждевременная оптимизация - корень всего зла. Но мы не должны упускать наши возможности в этих критических 3% ».

Профилирование кода

Прежде всего, вы должны найти узкое место вашего сценария и отметить, что никакая оптимизация не может компенсировать плохой выбор структуры данных или недостаток в дизайне вашего алгоритма. Во-вторых, не пытайтесь оптимизировать слишком рано в процессе кодирования за счет удобочитаемости / дизайна / качества. Дональд Кнут сделал следующее заявление об оптимизации:

«Мы должны забыть о небольшой эффективности, скажем, примерно в 97% случаев: преждевременная оптимизация - это корень всего зла, но мы не должны упускать наши возможности в этих критических 3%

Для cProfile вашего кода у вас есть несколько инструментов: cProfile (или более медленный profile ) из стандартной библиотеки, line_profiler и timeit . Каждый из них служит другой цели.

cProfile - это детерминированный профилировщик: отслеживаются вызов функции, возврат функции и события исключения, а также точные тайминги для интервалов между этими событиями (до 0,001 с). Документация библиотеки ([ https://docs.python.org/2/library/profile.html][1]) предоставляет нам простой пример использования

import cProfile
def f(x):
    return "42!"
cProfile.run('f(12)')

Или, если вы предпочитаете обертывать части вашего существующего кода:

import cProfile, pstats, StringIO
pr = cProfile.Profile()
pr.enable()
# ... do something ...
# ... long ...
pr.disable()
sortby = 'cumulative'
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print s.getvalue()

Это создаст результаты, похожие на таблицу ниже, где вы сможете быстро увидеть, где ваша программа проводит большую часть своего времени, и определить функции для оптимизации.

         3 function calls in 0.000 seconds

Ordered by: standard name
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000    0.000    0.000 <stdin>:1(f)
     1    0.000    0.000    0.000    0.000 <string>:1(<module>)
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

Модуль line_profiler ([ https://github.com/rkern/line_profiler][1]) полезен для линейного анализа вашего кода. Очевидно, что это невозможно для длинных скриптов, но нацелено на фрагменты. Дополнительную информацию см. В документации. Самый простой способ начать - использовать скрипт kernprof, как описано на странице пакета, обратите внимание, что вам нужно будет вручную указать функцию (функции) для профиля.

$ kernprof -l script_to_profile.py

kernprof создаст экземпляр LineProfiler и вставляет его в пространство имен __builtins__ с профилем имени. Он был написан для использования в качестве декоратора, поэтому в вашем скрипте вы украшаете функции, которые хотите профилировать с помощью @profile .

@profile
def slow_function(a, b, c):
    ...

Поведение kernprof по умолчанию заключается в том, чтобы поместить результаты в двоичный файл script_to_profile.py.lprof . Вы можете сказать kernprof немедленно просмотреть отформатированные результаты на терминале с опцией [-v / - view]. В противном случае вы можете просмотреть результаты позже так:

$ python -m line_profiler script_to_profile.py.lprof

Наконец, timeit предоставляет простой способ протестировать один лайнер или небольшое выражение как из командной строки, так и из оболочки python. Этот модуль ответит на вопрос, например: быстрее ли выполнять понимание списка или использовать встроенный list() при преобразовании набора в список. Найдите ключевое слово setup или -s чтобы добавить установочный код.

>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.8187260627746582

от терминала

$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop