Skip to content

Add a new memleak script that does everything #5360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 5, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add a new memleak script that does everything
This replaces our 4 memleak scripts with one that is able to test any
backend, with or without plot content, and with or without interactive
mode.

The calculation of average increase per iteration has been fixed.
Before, it assumed the increase was monotonically increasing, when in
fact it flucuates quite a bit.  Therefore, it now calculates the
difference between each pair of results and averages that.

Also, the results are stored in pre-allocated Numpy arrays rather than
Python lists to avoid including the increasing size of the Python lists
in the results.
  • Loading branch information
mdboom committed Nov 2, 2015
commit 05c347f991feb8e43334e5de50005fd8d74916bd
62 changes: 0 additions & 62 deletions unit/agg_memleak.py

This file was deleted.

122 changes: 122 additions & 0 deletions unit/memleak.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/usr/bin/env python

from __future__ import print_function

import gc
import tracemalloc

import numpy as np


def run_memleak_test(bench, iterations, report):
from matplotlib.cbook import report_memory

tracemalloc.start()

starti = min(10, iterations / 2)
endi = iterations

malloc_arr = np.empty((endi,), dtype=np.int64)
rss_arr = np.empty((endi,), dtype=np.int64)
nobjs_arr = np.empty((endi,), dtype=np.int64)
garbage_arr = np.empty((endi,), dtype=np.int64)

for i in range(endi):
bench()

gc.collect()
rss = report_memory()
malloc, peak = tracemalloc.get_traced_memory()
nobjs = len(gc.get_objects())
garbage = len(gc.garbage)
print("{0: 4d}: pymalloc {1: 10d}, rss {2: 10d}, nobjs {3: 10d}, garbage {4: 10d}".format(
i, malloc, rss, nobjs, garbage))

malloc_arr[i] = malloc
rss_arr[i] = rss
nobjs_arr[i] = nobjs
garbage_arr[i] = garbage

print('Average memory consumed per loop: %1.4f bytes\n' %
(np.sum(rss_arr[starti+1:] - rss_arr[starti:-1]) / float(endi - starti)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like just the sum of the differences, which is the end value minus the start value.

((rss_arr[-1] - rss_arr[starti]) / float(endi - starti))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose that's true. We need a different mechanism, then -- something that will take into account the spikiness of the data. If you select the start and end points incorrectly here you get wildly different results.

report


from matplotlib import pyplot as plt
fig, (ax1, ax2) = plt.subplots(2)
ax3 = ax1.twinx()
ax1.plot(malloc_arr[5:], 'r')
ax3.plot(rss_arr[5:], 'b')
ax1.set_ylabel('pymalloc', color='r')
ax3.set_ylabel('rss', color='b')

ax4 = ax2.twinx()
ax2.plot(nobjs_arr[5:], 'r')
ax4.plot(garbage_arr[5:], 'b')
ax2.set_ylabel('total objects', color='r')
ax4.set_ylabel('garbage objects', color='b')

if not report.endswith('.pdf'):
report = report + '.pdf'
fig.savefig(report, format='pdf')


class MemleakTest(object):
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('tmp', dpi=75)
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:
from matplotlib import pyplot as plt
plt.ion()

run_memleak_test(MemleakTest(args.empty), args.iterations[0], args.report[0])
103 changes: 0 additions & 103 deletions unit/memleak_gui.py

This file was deleted.

61 changes: 0 additions & 61 deletions unit/memleak_hawaii3.py

This file was deleted.

19 changes: 0 additions & 19 deletions unit/memleak_nongui.py

This file was deleted.