제가 사용해 본 메모리 프로파일링법은 두 가지가 있습니다.
경험 상의 장단점은 다음과 같았습니다.
대표적인 사용법은 python docs에 잘 설명이 되어 있고 이를 참조하였습니다.
start()
함수를 실행한 순간부터 메모리 사용량을 추적하기 시작합니다.
tracemalloc.start()
take_snapshot()
함수를 사용하면 그 시점에서의 메모리 현황을 저장합니다.
tracemalloc.take_snapshot()
다음 함수를 사용하면 분석 데이터를 추출할 수 있습니다.
snapshot.statistics('lineno')
하지만 여기서 얻은 데이터를 그대로 출력하면 다음과 같이 불필요한 정보와 함께 단순히 나열되기 때문에 한 눈에 파악하기 어렵습니다.
<frozen importlib._bootstrap>:716: size=4855 KiB, count=39328, average=126 B
<frozen importlib._bootstrap>:284: size=521 KiB, count=3199, average=167 B
/usr/lib/python3.4/collections/__init__.py:368: size=244 KiB, count=2315, average=108 B
/usr/lib/python3.4/unittest/case.py:381: size=185 KiB, count=779, average=243 B
/usr/lib/python3.4/unittest/case.py:402: size=154 KiB, count=378, average=416 B
/usr/lib/python3.4/abc.py:133: size=88.7 KiB, count=347, average=262 B
<frozen importlib._bootstrap>:1446: size=70.4 KiB, count=911, average=79 B
<frozen importlib._bootstrap>:1454: size=52.0 KiB, count=25, average=2131 B
<string>:5: size=49.7 KiB, count=148, average=344 B
/usr/lib/python3.4/sysconfig.py:411: size=48.0 KiB, count=1, average=48.0 KiB
따라서 docs 에서는 다음과 같은 함수를 사용하여 깨끗하게 출력할 수 있는 예제를 제공합니다.
import linecache
import os
import tracemalloc
def display_top(snapshot, key_type='lineno', limit=10):
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]
print("#%s: %s:%s: %.1f KiB"
% (index, frame.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()
'''
... run your application ...
'''
snapshot = tracemalloc.take_snapshot()
display_top(snapshot)
마찬가지로 PyPI의 documentation에 예제와 사용법이 잘 정리되어 있습니다.
터미널에 사용 중인 환경에서 다음과 같은 명령어를 입력하여 패키지를 설치하여 줍니다.
$ pip install memory_profiler
반드시 다음과 같이 프로파일링을 하고자 하는 코드를 함수로 만들고 앞에 @profile
annotation을 추가하여야 추적이 가능합니다.
# main.py
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
if __name__ == '__main__':
my_func()
파이썬 코드를 실행 시, -m memory_profiler
인수를 추가하면 데코레이션을 추가한 함수보다 깊은 호출 계층은 추적하지 않고, 코드별 메모리의 총 사용량과 증감을 계산하여줍니다.
하지만 반드시 정상적으로 코드가 종료 되어야 결과가 출력되기 때문에 런타임이 너무 긴 프로그램이나 대형 프로젝트에는 적절하지 않을 수 있습니다.
$ python -m memory_profiler main.py
ine # Mem usage Increment Occurences Line Contents
============================================================
3 38.816 MiB 38.816 MiB 1 @profile
4 def my_func():
5 46.492 MiB 7.676 MiB 1 a = [1] * (10 ** 6)
6 199.117 MiB 152.625 MiB 1 b = [2] * (2 * 10 ** 7)
7 46.629 MiB -152.488 MiB 1 del b
8 46.629 MiB 0.000 MiB 1 return a