Skip to content

Commit

Permalink
WIP: Reduce memory usage in doc build (mne-tools#9448)
Browse files Browse the repository at this point in the history
* WIP: Show problem [circle front]

* restore scraping

* Revert "restore scraping"

This reverts commit 045fbff.

* Fix

* Fix [circle full]

Co-authored-by: Guillaume Favelier <[email protected]>
  • Loading branch information
larsoner and GuillaumeFavelier authored Jun 10, 2021
1 parent c0d431a commit b05983e
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 43 deletions.
71 changes: 40 additions & 31 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,15 +286,30 @@ def __call__(self, gallery_conf, fname):
from pyvista import Plotter # noqa
except ImportError:
Plotter = None # noqa
try:
from pyvistaqt import BackgroundPlotter # noqa
except ImportError:
BackgroundPlotter = None # noqa
try:
from vtk import vtkPolyData # noqa
except ImportError:
vtkPolyData = None # noqa
from mne.viz.backends.renderer import backend
_Renderer = backend._Renderer if backend is not None else None
reset_warnings(gallery_conf, fname)
# in case users have interactive mode turned on in matplotlibrc,
# turn it off here (otherwise the build can be very slow)
plt.ioff()
plt.rcParams['animation.embed_limit'] = 30.
gc.collect()
# _assert_no_instances(Brain, 'running') # calls gc.collect()
# if Plotter is not None:
# _assert_no_instances(Plotter, 'running')
_assert_no_instances(Brain, 'Brain') # calls gc.collect()
if Plotter is not None:
_assert_no_instances(Plotter, 'Plotter')
if BackgroundPlotter is not None:
_assert_no_instances(BackgroundPlotter, 'BackgroundPlotter')
if vtkPolyData is not None:
_assert_no_instances(vtkPolyData, 'vtkPolyData')
_assert_no_instances(_Renderer, '_Renderer')
# This will overwrite some Sphinx printing but it's useful
# for memory timestamps
if os.getenv('SG_STAMP_STARTS', '').lower() == 'true':
Expand All @@ -309,38 +324,32 @@ def __call__(self, gallery_conf, fname):
os.environ['_MNE_BUILDING_DOC'] = 'true'
scrapers = ('matplotlib',)
try:
mlab = mne.utils._import_mlab()
# Do not pop up any mayavi windows while running the
# examples. These are very annoying since they steal the focus.
mlab.options.offscreen = True
# hack to initialize the Mayavi Engine
mlab.test_plot3d()
mlab.close()
except Exception:
pass
else:
scrapers += ('mayavi',)
try:
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
import pyvista
pyvista.OFF_SCREEN = False
mne.viz.set_3d_backend(mne.viz.get_3d_backend())
except Exception:
pass
report_scraper = None
else:
scrapers += ('pyvista',)
if any(x in scrapers for x in ('pyvista', 'mayavi')):
from traits.api import push_exception_handler
push_exception_handler(reraise_exceptions=True)
backend = mne.viz.get_3d_backend()
if backend == 'mayavi':
from traits.api import push_exception_handler
mlab = mne.utils._import_mlab()
# Do not pop up any mayavi windows while running the
# examples. These are very annoying since they steal the focus.
mlab.options.offscreen = True
# hack to initialize the Mayavi Engine
mlab.test_plot3d()
mlab.close()
scrapers += ('mayavi',)
push_exception_handler(reraise_exceptions=True)
elif backend in ('notebook', 'pyvista'):
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
import pyvista
pyvista.OFF_SCREEN = False
brain_scraper = mne.viz._brain._BrainScraper()
scrapers += (brain_scraper, 'pyvista')
report_scraper = mne.report._ReportScraper()
scrapers += (report_scraper,)
else:
report_scraper = None
if 'pyvista' in scrapers:
brain_scraper = mne.viz._brain._BrainScraper()
scrapers = list(scrapers)
scrapers.insert(scrapers.index('pyvista'), brain_scraper)
scrapers = tuple(scrapers)
del backend

sphinx_gallery_conf = {
'doc_module': ('mne',),
Expand Down
6 changes: 6 additions & 0 deletions mne/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,12 @@ def _assert_no_instances(cls, when=''):
not inspect.isframe(r):
if isinstance(r, (list, dict)):
rep = f'len={len(r)}'
r_ = gc.get_referrers(r)
types = (x.__class__.__name__ for x in r_)
types = "/".join(sorted(set(
x for x in types if x is not None)))
rep += f', {len(r_)} referrers: {types}'
del r_
else:
rep = repr(r)[:100].replace('\n', ' ')
ref.append(f'{r.__class__.__name__}: {rep}')
Expand Down
22 changes: 15 additions & 7 deletions mne/viz/backends/_pyvista.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,17 @@ def __init__(self, fig=None, size=(600, 600), bgcolor='black',

@property
def _all_plotters(self):
return [self.figure.plotter]
if self.figure.plotter is not None:
return [self.figure.plotter]
else:
return list()

@property
def _all_renderers(self):
return self.figure.plotter.renderers
if self.figure.plotter is not None:
return self.figure.plotter.renderers
else:
return list()

def _hide_axes(self):
for renderer in self._all_renderers:
Expand Down Expand Up @@ -1014,13 +1020,15 @@ def _check_3d_figure(figure):
def _close_3d_figure(figure):
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=FutureWarning)
# copy the plotter locally because figure.plotter is modified
plotter = figure.plotter
# close the window
figure.plotter.close()
_process_events(figure.plotter)
plotter.close() # additional cleaning following signal_close
_process_events(plotter)
# free memory and deregister from the scraper
figure.plotter.deep_clean()
del _ALL_PLOTTERS[figure.plotter._id_name]
_process_events(figure.plotter)
plotter.deep_clean() # remove internal references
del _ALL_PLOTTERS[plotter._id_name]
_process_events(plotter)


def _take_3d_screenshot(figure, mode='rgb', filename=None):
Expand Down
5 changes: 5 additions & 0 deletions mne/viz/backends/_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,11 @@ def _window_initialize(self):
self._interactor = self.figure.plotter.interactor
self._window = self.figure.plotter.app_window
self._window.setLocale(QLocale(QLocale.Language.English))
self._window.signal_close.connect(self._window_clean)

def _window_clean(self):
self.figure.plotter = None
self._interactor = None

def _window_close_connect(self, func):
self._window.signal_close.connect(func)
Expand Down
5 changes: 0 additions & 5 deletions tutorials/forward/35_eeg_no_mri.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,6 @@
bem=bem, eeg=True, mindist=5.0, n_jobs=1)
print(fwd)

# Use fwd to compute the sensitivity map for illustration purposes
eeg_map = mne.sensitivity_map(fwd, ch_type='eeg', mode='fixed')
brain = eeg_map.plot(time_label='EEG sensitivity', subjects_dir=subjects_dir,
clim=dict(lims=[5, 50, 100]))

##############################################################################
# From here on, standard inverse imaging methods can be used!
#
Expand Down

0 comments on commit b05983e

Please sign in to comment.