Как профилировать использование памяти в Python?
недавно я заинтересовался алгоритмами и начал изучать их, написав наивную реализацию, а затем оптимизируя ее различными способами.
Я уже знаком со стандартным модулем Python для профилирования среды выполнения (для большинства вещей я нашел магическую функцию timeit в IPython достаточной), но меня также интересует использование памяти, поэтому я могу изучить эти компромиссы (например, стоимость кэширования таблицы ранее вычисленных значений по сравнению с пересчитывая их по мере необходимости). Есть ли модуль, который будет профилировать использование памяти данной функции для меня?
6 ответов
на этот вопрос уже ответили здесь:профилировщик памяти Python
в основном вы делаете что-то вроде этого (взято с гуппи-ЧП):
>>> from guppy import hpy; h=hpy()
>>> h.heap()
Partition of a set of 48477 objects. Total size = 3265516 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 25773 53 1612820 49 1612820 49 str
1 11699 24 483960 15 2096780 64 tuple
2 174 0 241584 7 2338364 72 dict of module
3 3478 7 222592 7 2560956 78 types.CodeType
4 3296 7 184576 6 2745532 84 function
5 401 1 175112 5 2920644 89 dict of class
6 108 0 81888 3 3002532 92 dict (no owner)
7 114 0 79632 2 3082164 94 dict of type
8 117 0 51336 2 3133500 96 type
9 667 1 24012 1 3157512 97 __builtin__.wrapper_descriptor
<76 more rows. Type e.g. '_.more' to view.>
>>> h.iso(1,[],{})
Partition of a set of 3 objects. Total size = 176 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1 33 136 77 136 77 dict (no owner)
1 1 33 28 16 164 93 list
2 1 33 12 7 176 100 int
>>> x=[]
>>> h.iso(x).sp
0: h.Root.i0_modules['__main__'].__dict__['x']
>>>
Python 3.4 включает в себя новый модуль:tracemalloc
. Он предоставляет подробную статистику о том, какой код выделения памяти. Вот пример, который отображает три верхние строки, выделяющие память.
from collections import Counter
import linecache
import os
import tracemalloc
def display_top(snapshot, key_type='lineno', limit=3):
snapshot = snapshot.filter_traces((
tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
tracemalloc.Filter(False, "<unknown>"),
))
top_stats = snapshot.statistics(key_type)
print("Top %s lines" % limit)
for index, stat in enumerate(top_stats[:limit], 1):
frame = stat.traceback[0]
# replace "/path/to/module/file.py" with "module/file.py"
filename = os.sep.join(frame.filename.split(os.sep)[-2:])
print("#%s: %s:%s: %.1f KiB"
% (index, filename, frame.lineno, stat.size / 1024))
line = linecache.getline(frame.filename, frame.lineno).strip()
if line:
print(' %s' % line)
other = top_stats[limit:]
if other:
size = sum(stat.size for stat in other)
print("%s other: %.1f KiB" % (len(other), size / 1024))
total = sum(stat.size for stat in top_stats)
print("Total allocated size: %.1f KiB" % (total / 1024))
tracemalloc.start()
counts = Counter()
fname = '/usr/share/dict/american-english'
with open(fname) as words:
words = list(words)
for word in words:
prefix = word[:3]
counts[prefix] += 1
print('Top prefixes:', counts.most_common(3))
snapshot = tracemalloc.take_snapshot()
display_top(snapshot)
и вот результаты:
Top prefixes: [('con', 1220), ('dis', 1002), ('pro', 809)]
Top 3 lines
#1: scratches/memory_test.py:37: 6527.1 KiB
words = list(words)
#2: scratches/memory_test.py:39: 247.7 KiB
prefix = word[:3]
#3: scratches/memory_test.py:40: 193.0 KiB
counts[prefix] += 1
4 other: 4.3 KiB
Total allocated size: 6972.1 KiB
когда утечка памяти утечка?
этот пример великолепен, когда память все еще удерживается в конце вычисления, но иногда у вас есть код, который выделяет много памяти, а затем освобождает все это. Технически это не утечка памяти, но она использует больше памяти, чем вы думаете. Как вы можете отслеживать использование памяти, когда это все выйдет? Если это ваш код, вы можете добавить отладочный код для создания моментальных снимков во время его работы. Если нет, можно запустить фоновый поток для мониторинга использования памяти во время выполнения основного потока.
вот предыдущий пример, где код был перемещен в
для действительно простого подхода попробуйте:
import resource
def using(point=""):
usage=resource.getrusage(resource.RUSAGE_SELF)
return '''%s: usertime=%s systime=%s mem=%s mb
'''%(point,usage[0],usage[1],
(usage[2]*resource.getpagesize())/1000000.0 )
просто вставить using("Label")
где вы хотите увидеть, что происходит.
Я вы только хотите посмотреть на использование памяти объекта, (ответ на вопрос)
есть модуль под названием Pympler, которая содержит
asizeof
модуль.использовать следующим образом:
from pympler import asizeof asizeof.asizeof(my_object)
в отличие от
sys.getsizeof
, это работает на самостоятельно созданных объектов.>>> asizeof.asizeof(tuple('bcd')) 200 >>> asizeof.asizeof({'foo': 'bar', 'baz': 'bar'}) 400 >>> asizeof.asizeof({}) 280 >>> asizeof.asizeof({'foo':'bar'}) 360 >>> asizeof.asizeof('foo') 40 >>> asizeof.asizeof(Bar()) 352 >>> asizeof.asizeof(Bar().__dict__) 280
>>> help(asizeof.asizeof)
Help on function asizeof in module pympler.asizeof:
asizeof(*objs, **opts)
Return the combined size in bytes of all objects passed as positional arguments.
может это поможет:
посмотреть дополнительные>
pip install gprof2dot
sudo apt-get install graphviz
gprof2dot -f pstats profile_for_func1_001 | dot -Tpng -o profile.png
def profileit(name):
"""
@profileit("profile_for_func1_001")
"""
def inner(func):
def wrapper(*args, **kwargs):
prof = cProfile.Profile()
retval = prof.runcall(func, *args, **kwargs)
# Note use of name from outer scope
prof.dump_stats(name)
return retval
return wrapper
return inner
@profileit("profile_for_func1_001")
def func1(...)
Ниже приведен простой декоратор функций, который позволяет отслеживать, сколько памяти процесс потребляется до вызова функции, после вызова функции, и в чем разница:
import time
import os
import psutil
def elapsed_since(start):
return time.strftime("%H:%M:%S", time.gmtime(time.time() - start))
def get_process_memory():
process = psutil.Process(os.getpid())
return process.get_memory_info().rss
def profile(func):
def wrapper(*args, **kwargs):
mem_before = get_process_memory()
start = time.time()
result = func(*args, **kwargs)
elapsed_time = elapsed_since(start)
mem_after = get_process_memory()
print("{}: memory before: {:,}, after: {:,}, consumed: {:,}; exec time: {}".format(
func.__name__,
mem_before, mem_after, mem_after - mem_before,
elapsed_time))
return result
return wrapper
мой блог, который описывает все подробности.