1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
#!/usr/bin/env python
import gc
from io import BytesIO
import tracemalloc
try:
import psutil
except ImportError as err:
raise ImportError("This script requires psutil") from err
import numpy as np
def run_memleak_test(bench, iterations, report):
tracemalloc.start()
starti = min(50, iterations // 2)
endi = iterations
malloc_arr = np.empty(endi, dtype=np.int64)
rss_arr = np.empty(endi, dtype=np.int64)
rss_peaks = np.empty(endi, dtype=np.int64)
nobjs_arr = np.empty(endi, dtype=np.int64)
garbage_arr = np.empty(endi, dtype=np.int64)
open_files_arr = np.empty(endi, dtype=np.int64)
rss_peak = 0
p = psutil.Process()
for i in range(endi):
bench()
gc.collect()
rss = p.memory_info().rss
malloc, peak = tracemalloc.get_traced_memory()
nobjs = len(gc.get_objects())
garbage = len(gc.garbage)
open_files = len(p.open_files())
print(f"{i: 4d}: pymalloc {malloc: 10d}, rss {rss: 10d}, "
f"nobjs {nobjs: 10d}, garbage {garbage: 4d}, "
f"files: {open_files: 4d}")
if i == starti:
print(f'{" warmup done ":-^86s}')
malloc_arr[i] = malloc
rss_arr[i] = rss
if rss > rss_peak:
rss_peak = rss
rss_peaks[i] = rss_peak
nobjs_arr[i] = nobjs
garbage_arr[i] = garbage
open_files_arr[i] = open_files
print('Average memory consumed per loop: {:1.4f} bytes\n'.format(
np.sum(rss_peaks[starti+1:] - rss_peaks[starti:-1]) / (endi - starti)))
from matplotlib import pyplot as plt
from matplotlib.ticker import EngFormatter
bytes_formatter = EngFormatter(unit='B')
fig, (ax1, ax2, ax3) = plt.subplots(3)
for ax in (ax1, ax2, ax3):
ax.axvline(starti, linestyle='--', color='k')
ax1b = ax1.twinx()
ax1b.yaxis.set_major_formatter(bytes_formatter)
ax1.plot(malloc_arr, 'C0')
ax1b.plot(rss_arr, 'C1', label='rss')
ax1b.plot(rss_peaks, 'C1', linestyle='--', label='rss max')
ax1.set_ylabel('pymalloc', color='C0')
ax1b.set_ylabel('rss', color='C1')
ax1b.legend()
ax2b = ax2.twinx()
ax2.plot(nobjs_arr, 'C0')
ax2b.plot(garbage_arr, 'C1')
ax2.set_ylabel('total objects', color='C0')
ax2b.set_ylabel('garbage objects', color='C1')
ax3.plot(open_files_arr)
ax3.set_ylabel('open file handles')
if not report.endswith('.pdf'):
report = report + '.pdf'
fig.tight_layout()
fig.savefig(report, format='pdf')
class MemleakTest:
def __init__(self, empty):
self.empty = empty
def __call__(self):
import matplotlib.pyplot as plt
fig = plt.figure(1)
if not self.empty:
t1 = np.arange(0.0, 2.0, 0.01)
y1 = np.sin(2 * np.pi * t1)
y2 = np.random.rand(len(t1))
X = np.random.rand(50, 50)
ax = fig.add_subplot(221)
ax.plot(t1, y1, '-')
ax.plot(t1, y2, 's')
ax = fig.add_subplot(222)
ax.imshow(X)
ax = fig.add_subplot(223)
ax.scatter(np.random.rand(50), np.random.rand(50),
s=100 * np.random.rand(50), c=np.random.rand(50))
ax = fig.add_subplot(224)
ax.pcolor(10 * np.random.rand(50, 50))
fig.savefig(BytesIO(), dpi=75)
fig.canvas.flush_events()
plt.close(1)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser('Run memory leak tests')
parser.add_argument('backend', type=str, nargs=1,
help='backend to test')
parser.add_argument('iterations', type=int, nargs=1,
help='number of iterations')
parser.add_argument('report', type=str, nargs=1,
help='filename to save report')
parser.add_argument('--empty', action='store_true',
help="Don't plot any content, just test creating "
"and destroying figures")
parser.add_argument('--interactive', action='store_true',
help="Turn on interactive mode to actually open "
"windows. Only works with some GUI backends.")
args = parser.parse_args()
import matplotlib
matplotlib.use(args.backend[0])
if args.interactive:
import matplotlib.pyplot as plt
plt.ion()
run_memleak_test(
MemleakTest(args.empty), args.iterations[0], args.report[0])
|