Зазвичай я використовую команду shell 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
Memory Profiler для всіх ваших потреб у пам’яті.
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
Приклади наведені у документах, зв’язаних вище.
Погляньте на ніс та на один із його плагінів, цей зокрема.
Після встановлення, nose - це скрипт на вашому шляху, і ви можете викликати його в каталог, який містить кілька сценаріїв 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
вивід, оскільки вони можуть складати лише загальну кількість разів для кожної функції.
Однак загалом нам дійсно потрібно побачити вкладений вигляд, що містить сліди стеку кожного виклику, щоб насправді легко знайти основні вузькі місця.
І це саме те, що пропонує snakeviz за допомогою типового подання "бурулька".
Спочатку вам потрібно скинути дані cProfile у двійковий файл, а потім ви можете на цьому зміг
pip install -u snakeviz
python -m cProfile -o results.prof myscript.py
snakeviz results.prof
Це друкує URL-адресу на stdout, яку ви можете відкрити у своєму браузері, яка містить бажаний результат, який виглядає так:
і ви можете:
Більш орієнтоване на профіль запитання: Як ви можете профілювати скрипт 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
. Потім файл oputput може бути дуже приємно представлений у браузері програмою під назвою SnakeViz за допомогоюsnakeviz results.prof