Skip to content

Commit 4f4af6d

Browse files
author
Jonathan Rocher
committed
Adding memory profiling demos and an exercise.
1 parent 4c066ba commit 4f4af6d

File tree

5 files changed

+147
-0
lines changed

5 files changed

+147
-0
lines changed

memory_profile/mem_profile_test.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
""" File to test the memory profiler package. To run it,
2+
3+
python -m memory_profiler mem_profile_test.py
4+
"""
5+
6+
@profile
7+
def my_func():
8+
a = [1] * (10 ** 6)
9+
b = [2] * (2 * 10 ** 7)
10+
del b
11+
return a
12+
13+
if __name__ == '__main__':
14+
my_func()

memory_profile/memory_footprint.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""
2+
Write a function that takes an object and computes it total memory footprint.
3+
Test your function on
4+
- simple datastructures,
5+
- custom classes
6+
- numpy arrays.
7+
"""
8+
9+
from sys import getsizeof
10+
from gc import get_referents
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
""" Supporting function for memory profiler testing
2+
"""
3+
4+
from numpy import array, int64, float32
5+
6+
def my_func2(b):
7+
a = 1.0 # Negligible footprint on the memory usage
8+
c = array(b, dtype = int64)
9+
c += a
10+
c = c.astype(float32)
11+
return c
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
""" Demo file for testing memory profiling with memory_profiler and heapy.
2+
3+
"""
4+
# Line below is necessary to run the code without -m memory_profiler
5+
from memory_profiler import profile
6+
from memory_profiler_support import my_func2
7+
8+
9+
@profile
10+
def my_func():
11+
result = {}
12+
a = [1] * (10 ** 6)
13+
b = [2] * (2 * 10 ** 7)
14+
c = my_func2(b)
15+
del b
16+
result["a"] = a
17+
result["c"] = c
18+
return result
19+
20+
class MyClass(object):
21+
def __init__(self, x, y):
22+
self.x = x
23+
self.y = y
24+
self.a = linspace(self.y)
25+
26+
if __name__ == '__main__':
27+
a = my_func()
28+
29+
try:
30+
from guppy import hpy
31+
hp = hpy()
32+
h1 = hp.heap()
33+
print h1
34+
print h1.bymodule
35+
except ImportError:
36+
print "heapy is not installed on your machine"

memory_profile/simple_heap.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
""" Simple class to track down the memory usage of an object, including the
2+
objects it points to that are likely to be part of the object.
3+
4+
Based on an original version by Robert Kern
5+
"""
6+
from collections import deque
7+
import gc
8+
import sys
9+
import types
10+
11+
from traits.api import Any, HasTraits, Int, Set
12+
from numpy import ndarray, dtype
13+
14+
class SimpleHeap(HasTraits):
15+
""" A simple heap memory tracker.
16+
"""
17+
18+
# The root object we are trying to ascertain the size of.
19+
root = Any()
20+
21+
# The total size of the rooted object graph.
22+
size = Int()
23+
24+
# The IDs of objects that have been seen.
25+
_seen = Set(Int)
26+
27+
def __init__(self, root, **traits):
28+
super(SimpleHeap, self).__init__(root=root, **traits)
29+
30+
self.compute_size()
31+
32+
def compute_size(self):
33+
queue = deque([self.root])
34+
while queue:
35+
self.size += self._pop_and_size(queue)
36+
print "Size of the object and what it contains is %s bytes." % self.size
37+
38+
def _pop_and_size(self, queue):
39+
""" Pop an item off the queue and return its size. Add its referents to
40+
the queue.
41+
"""
42+
obj = queue.popleft()
43+
if id(obj) in self._seen:
44+
return 0
45+
else:
46+
self._seen.add(id(obj))
47+
if isinstance(obj, ndarray):
48+
# The elements of a numpy array are not seen by the gc as a referent
49+
container_size = sys.getsizeof(obj)
50+
if obj.dtype != dtype('object'):
51+
return container_size + obj.nbytes
52+
else:
53+
# In this case, the element is a pointer (4 bytes).
54+
# Convert to a list to find the referents.
55+
referents = self._filtered_referents(obj.tolist())
56+
queue.extend(referents)
57+
return container_size
58+
else:
59+
size = sys.getsizeof(obj)
60+
referents = self._filtered_referents(obj)
61+
queue.extend(referents)
62+
return size
63+
64+
@staticmethod
65+
def _filtered_referents(obj):
66+
""" Return the referents of an object that we consider as "part"
67+
of the object using the garbage collector.
68+
"""
69+
referents = gc.get_referents(obj)
70+
badtypes = (types.CodeType, types.ModuleType, types.FunctionType,
71+
types.ClassType, types.TypeType, types.FrameType,
72+
types.GeneratorType, types.GetSetDescriptorType, types.LambdaType,
73+
types.MemberDescriptorType, types.MethodType, types.TracebackType,
74+
types.UnboundMethodType)
75+
good = [x for x in referents if not isinstance(x, badtypes)]
76+
return good

0 commit comments

Comments
 (0)