Memory Profiling
Managing memory is important in any programming logic but this becomes necessary for python. As python is used in Ml and AI where vast data are used which needs to be managed. Memory leaks, i.e. the program is out of memory after running for several hours. To manage these memory leaks memory monitoring is essential. Monitoring memory is also called profiling. As a developer, it’s a necessity that we profile our program and use less memory allocation as much as possible.
Using Tracemalloc
Tracemalloc is a library module that traces every memory block in python. The tracing starts by using the start() during runtime. This library module can also give information about the total size, number, and average size of allocated memory blocks.
# importing the module
import tracemalloc
# code or function for which memory
# has to be monitored
def app():
lt = []
for i in range(0, 100000):
lt.append(i)
# starting the monitoring
tracemalloc.start()
# function call
app()
# displaying the memory
print(tracemalloc.get_traced_memory())
# stopping the library
tracemalloc.stop()
(729, 3617551)
The output is given in form of (current, peak), i.e. current memory is the memory the code is currently using and peak memory is the maximum space the program used while executing.
Using Psutil
Psutil is a python system library used to keep track of various resources in the system and their utilization. The library is used for profiling, limiting, and management of process resources.
import os
import psutil
p = psutil.Process(os.getpid())
for i in range(10):
print(i)
for j in range(5):
mem_usage = p.memory_info().rss / 1024 / 1024
print("{} {} MB".format(j, mem_usage))
def memory_usage_psutil():
# return the memory usage in MB
import psutil
process = psutil.Process(os.getpid())
mem = process.get_memory_info()[0] / float(2 ** 20)
return mem
If you are looking for an easy way to get the memory consumption within Python this in my opinion your best shot.
Using Memory Profiler
!pip install memory-profiler
%%file sos.py
"""memory_profiler example"""
@profile
def sum_of_diffs(vals):
"""Compute sum of diffs"""
vals2 = vals[1:]
total = 0
for v1, v2 in zip(vals, vals2):
total += v2 - v1
return total
if __name__ == '__main__':
vals = list(range(1, 1_000_000, 3))
print(sum_of_diffs(vals))
Writing sos.py
!python -m memory_profiler sos
999996
Filename: /content/sos.py
Line # Mem usage Increment Occurrences Line Contents
=============================================================
3 52.160 MiB 52.160 MiB 1 @profile
4 def sum_of_diffs(vals):
5 """Compute sum of diffs"""
6 54.738 MiB 2.578 MiB 1 vals2 = vals[1:]
7
8 54.738 MiB 0.000 MiB 1 total = 0
9 54.738 MiB 0.000 MiB 333333 for v1, v2 in zip(vals, vals2):
10 54.738 MiB 0.000 MiB 333332 total += v2 - v1
11
12 54.738 MiB 0.000 MiB 1 return total
%%file sos1.py
"""memory_profiler example"""
def sum_of_diffs(vals):
"""Compute sum of diffs"""
vals2 = vals[1:]
total = 0
for v1, v2 in zip(vals, vals2):
total += v2 - v1
return total
if __name__ == '__main__':
vals = list(range(1, 100_000_000, 3))
print(sum_of_diffs(vals))
Writing sos1.py
mprof generates profile data
!mprof run sos1.py
mprof: Sampling memory every 0.1s
running new process
running as a Python program...
99999996
!mprof plot mprofile_20211225174100.dat
<Figure size 1260x540 with 1 Axes>
Reducing Memory Consumption with Slots
Slots in Python is a special mechanism that is used to reduce memory of the objects. In Python, all the objects use a dynamic dictionary for adding an attribute. Slots is a static type method in this no dynamic dictionary are required for allocating attribute.
# defining the class.
class myCourse:
# defining the slots.
__slots__ =('course', 'price')
def __init__(self):
# initializing the values
self.course ='MAth'
self.price = 300
# create an object of gfg class
a = myCourse()
# print the slot
print(a.__slots__)
# print the slot variable
print(a.course, a.price)
('course', 'price')
MAth 300
#Example of python object without slots
class MFT(object):
def __init__(self, *args, **kwargs):
self.a = 1
self.b = 2
if __name__ == "__main__":
instance = MFT()
print(instance.__dict__)
{'a': 1, 'b': 2}
#Example of python object with slots
class MFT(object):
__slots__=['a', 'b']
def __init__(self, *args, **kwargs):
self.a = 1
self.b = 2
if __name__ == "__main__":
instance = MFT()
print(instance.__slots__)
['a', 'b']
Result of using slots:
- Fast access to attributes
- Saves memory space