Обычно я использую команду оболочки time
. Моя цель - проверить, является ли набор данных маленьким, средним, большим или очень большим, сколько времени и памяти будет использоваться.
Какие-нибудь инструменты для Linux или просто Python для этого?
Ответы:
Взгляните на timeit , профилировщик python и pycallgraph . Также не забудьте взглянуть на комментарий ниже,nikicc
упомянув " SnakeViz ". Это дает вам еще одну визуализацию данных профилирования, которая может быть полезной.
def test():
"""Stupid test function"""
lst = []
for i in range(100):
lst.append(i)
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test"))
# For Python>=3.5 one can also write:
print(timeit.timeit("test()", globals=locals()))
По сути, вы можете передать ему код Python в качестве строкового параметра, и он будет запускаться в указанное количество раз и печатать время выполнения. Важные моменты из документации :
timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)
СоздайтеTimer
экземпляр с данным оператором, кодом настройки и функцией таймера и запустите егоtimeit
метод с выполнением числа . Необязательный аргумент globals указывает пространство имен, в котором следует выполнить код.
... и:
Timer.timeit(number=1000000)
Время число казней основного утверждения. Он выполняет оператор настройки один раз, а затем возвращает время, необходимое для выполнения основного оператора несколько раз, измеренное в секундах как число с плавающей запятой. Аргумент - это количество проходов цикла, по умолчанию - один миллион. Конструктору передаются основной оператор, оператор настройки и функция таймера.Примечание. По умолчанию
timeit
временно отключаетсяgarbage collection
во время отсчета времени. Преимущество этого подхода в том, что он делает независимые тайминги более сопоставимыми. Этот недостаток состоит в том, что сборщик мусора может быть важным компонентом производительности измеряемой функции. Если это так, GC можно повторно включить в качестве первого оператора в строке настройки . Например:
timeit.Timer('for i in xrange(10): oct(i)', 'gc.enable()').timeit()
Профилирование даст вам гораздо более детальное представление о том, что происходит. Вот «мгновенный пример» из официальных документов :
import cProfile
import re
cProfile.run('re.compile("foo|bar")')
Что даст вам:
197 function calls (192 primitive calls) in 0.002 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.001 0.001 <string>:1(<module>)
1 0.000 0.000 0.001 0.001 re.py:212(compile)
1 0.000 0.000 0.001 0.001 re.py:268(_compile)
1 0.000 0.000 0.000 0.000 sre_compile.py:172(_compile_charset)
1 0.000 0.000 0.000 0.000 sre_compile.py:201(_optimize_charset)
4 0.000 0.000 0.000 0.000 sre_compile.py:25(_identityfunction)
3/1 0.000 0.000 0.000 0.000 sre_compile.py:33(_compile)
Оба этих модуля должны дать вам представление о том, где искать узкие места.
Также, чтобы разобраться с выводом profile
, прочтите этот пост
ПРИМЕЧАНИЕ. Pycallgraph был официально закрыт с февраля 2018 года . Однако по состоянию на декабрь 2020 года он все еще работал над Python 3.6. Тем не менее, пока нет основных изменений в том, как python предоставляет API профилирования, он должен оставаться полезным инструментом.
Этот модуль использует graphviz для создания таких графов вызовов:
Вы можете легко увидеть, какие пути израсходованы больше всего по цвету. Вы можете создать их с помощью API pycallgraph или с помощью упакованного скрипта:
pycallgraph graphviz -- ./mypythonscript.py
Однако накладные расходы весьма значительны. Поэтому для уже давно работающих процессов создание графа может занять некоторое время.
Я использую простой декоратор для задания времени функции
def st_time(func):
"""
st decorator to calculate the total time of a func
"""
def st_func(*args, **keyArgs):
t1 = time.time()
r = func(*args, **keyArgs)
t2 = time.time()
print "Function=%s, Time=%s" % (func.__name__, t2 - t1)
return r
return st_func
timeit
Модуль был медленным и странно, поэтому я написал это:
def timereps(reps, func):
from time import time
start = time()
for i in range(0, reps):
func()
end = time()
return (end - start) / reps
Пример:
import os
listdir_time = timereps(10000, lambda: os.listdir('/'))
print "python can do %d os.listdir('/') per second" % (1 / listdir_time)
Для меня это говорит:
python can do 40925 os.listdir('/') per second
Это примитивный вид тестирования, но он достаточно хорош.
Обычно я быстро time ./script.py
смотрю, сколько времени это займет. Это не показывает вам память, по крайней мере, не по умолчанию. Вы можете использовать /usr/bin/time -v ./script.py
для получения большого количества информации, в том числе об использовании памяти.
/usr/bin/time
с -v
опцией недоступна по умолчанию во многих дистрибутивах, ее необходимо установить. sudo apt-get install time
в debian, ubuntu и т. д. pacman -S time
archlinux
Профилировщик памяти для всех ваших потребностей в памяти.
https://pypi.python.org/pypi/memory_profiler
Запустите установку pip:
pip install memory_profiler
Импортируйте библиотеку:
import memory_profiler
Добавьте декоратор к элементу, который хотите профилировать:
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
if __name__ == '__main__':
my_func()
Выполните код:
python -m memory_profiler example.py
Получите вывод:
Line # Mem usage Increment Line Contents
==============================================
3 @profile
4 5.97 MB 0.00 MB def my_func():
5 13.61 MB 7.64 MB a = [1] * (10 ** 6)
6 166.20 MB 152.59 MB b = [2] * (2 * 10 ** 7)
7 13.61 MB -152.59 MB del b
8 13.61 MB 0.00 MB return a
Примеры взяты из документации, указанной выше.
Посмотрите на нос и на один из его плагинов, этот в частности.
После установки нос - это сценарий на вашем пути, который вы можете вызвать в каталоге, который содержит некоторые сценарии Python:
$: nosetests
Он будет искать во всех файлах python в текущем каталоге и выполнять любую функцию, которую распознает как тест: например, он распознает любую функцию со словом test_ в своем имени как тест.
Итак, вы можете просто создать скрипт python с именем test_yourfunction.py и написать в нем что-то вроде этого:
$: cat > test_yourfunction.py
def test_smallinput():
yourfunction(smallinput)
def test_mediuminput():
yourfunction(mediuminput)
def test_largeinput():
yourfunction(largeinput)
Тогда тебе нужно бежать
$: nosetest --with-profile --profile-stats-file yourstatsprofile.prof testyourfunction.py
и чтобы прочитать файл профиля, используйте эту строку python:
python -c "import hotshot.stats ; stats = hotshot.stats.load('yourstatsprofile.prof') ; stats.sort_stats('time', 'calls') ; stats.print_stats(200)"
nose
полагается на горячее. Он больше не поддерживается, начиная с Python 2.5, и сохраняется только «для специального использования»
Будьте осторожны, timeit
это очень медленно, на моем среднем процессоре требуется 12 секунд, чтобы просто инициализировать (или, возможно, запустить функцию). вы можете проверить этот принятый ответ
def test():
lst = []
for i in range(100):
lst.append(i)
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test")) # 12 second
time
вместо этого я буду использовать простую вещь , на моем ПК он возвращает результат0.0
import time
def test():
lst = []
for i in range(100):
lst.append(i)
t1 = time.time()
test()
result = time.time() - t1
print(result) # 0.000000xxxx
timeit
запускает вашу функцию много раз, чтобы усреднить шум. Количество повторов - это вариант, см. Сравнение времени выполнения в python или более позднюю часть принятого ответа на этот вопрос.
snakeviz
интерактивный просмотрщик для cProfile
https://github.com/jiffyclub/snakeviz/
cProfile был упомянут на https://stackoverflow.com/a/1593034/895245, а snakeviz был упомянут в комментарии , но я хотел выделить его дополнительно.
Очень сложно отладить производительность программы, просто взглянув на cprofile
/pstats
output, потому что они могут только общее время для каждой функции из коробки.
Однако в целом нам действительно нужно увидеть вложенное представление, содержащее трассировки стека каждого вызова, чтобы на самом деле легко найти основные узкие места.
И это именно то, что snakeviz предоставляет в виде «сосульки» по умолчанию.
Сначала вам нужно сбросить данные cProfile в двоичный файл, а затем вы можете использовать snakeviz на этом
pip install -u snakeviz
python -m cProfile -o results.prof myscript.py
snakeviz results.prof
Это напечатает URL-адрес стандартного вывода, который вы можете открыть в своем браузере, который содержит желаемый результат, который выглядит следующим образом:
и тогда вы можете:
Более профильный вопрос: как профилировать сценарий Python?
Если вы не хотите писать шаблонный код для timeit и легко анализировать результаты, взгляните на benchmarkit . Кроме того, он сохраняет историю предыдущих запусков, поэтому можно легко сравнить одну и ту же функцию в процессе разработки.
# pip install benchmarkit
from benchmarkit import benchmark, benchmark_run
N = 10000
seq_list = list(range(N))
seq_set = set(range(N))
SAVE_PATH = '/tmp/benchmark_time.jsonl'
@benchmark(num_iters=100, save_params=True)
def search_in_list(num_items=N):
return num_items - 1 in seq_list
@benchmark(num_iters=100, save_params=True)
def search_in_set(num_items=N):
return num_items - 1 in seq_set
benchmark_results = benchmark_run(
[search_in_list, search_in_set],
SAVE_PATH,
comment='initial benchmark search',
)
Печатает на терминал и возвращает список словарей с данными за последний запуск. Также доступны точки входа в командную строку.
Если вы измените N=1000000
и перезапустите
python -m cProfile -o results.prof myscript.py
. Затем файл вывода может быть очень хорошо представлен в браузере с помощью программы SnakeViz, использующейsnakeviz results.prof