Skip to content

Conversation

@GuillaumeFavelier
Copy link
Contributor

@GuillaumeFavelier GuillaumeFavelier commented Oct 21, 2020

The first goal is to collect all the remaining vtk objects:

mprof run mem_brain.py && mprof plot

mem_brain.py:

import gc
import time
import numpy as np
import mne
rr, tris = mne.read_surface(mne.datasets.sample.data_path() + '/subjects/fsaverage/surf/lh.inflated')
tris = np.pad(tris, ((0, 0), (1, 0)), 'constant')
assert tris.shape[1] == 4
tris[:, 0] = 3
for _ in range(1):
    brain = mne.viz.Brain('fsaverage', 'both', 'inflated')
    brain.close()
    del brain
    time.sleep(0.1)
    gc.collect()
    print([o.__class__.__name__ for o in gc.get_objects() if o.__class__.__name__.startswith('vtk')], len(gc.get_objects()))

To remove:

['vtkXOpenGLRenderWindow', 'vtkGenericRenderWindowInteractor', 'vtkLookupTable', 'vtkOpenGLActor']

The second goal is to improve Brain memory consumption to help with #8379

cc @larsoner

@GuillaumeFavelier GuillaumeFavelier self-assigned this Oct 21, 2020
@larsoner
Copy link
Member

I wonder if vtkGenericRenderWindowInteractor cannot be GC'ed because of AddObserver, see http://vtk.1045678.n5.nabble.com/VTK-memory-management-td5715661.html

Then the LookupTable / Actor is probably another (separate) problem? It's probably going to take more code commenting-out and line tracking to sort it out...

fig=figure)

if _get_3d_backend() == "pyvista":
self.plotter = self._renderer.plotter
Copy link
Member

Choose a reason for hiding this comment

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

Now that Brain is public we should probably make as many attributes as possible private, so _plotter is better here

@GuillaumeFavelier
Copy link
Contributor Author

And an automatic test would be to ensure that this list is actually empty.

@larsoner
Copy link
Member

And an automatic test would be to ensure that this list is actually empty.

If you can get the list empty, I can add this test :)

What I'll really probably do is port the gc-testing parts of #8379 here and add the vtk-object-GC check, then we merge this, then I rebase #8379 ...

@GuillaumeFavelier
Copy link
Contributor Author

Those 2 are added in BackgroundPlotter:

['vtkXOpenGLRenderWindow', 'vtkGenericRenderWindowInteractor']

And the rest is related to our use of add_mesh somehow:

['vtkLookupTable', 'vtkOpenGLActor']

@GuillaumeFavelier
Copy link
Contributor Author

c2f56b5 gets rid of the ref to vtkLookupTable but one vtkOpenGLActor remains.

@larsoner
Copy link
Member

See also pyvista/pyvista#958

@GuillaumeFavelier
Copy link
Contributor Author

GuillaumeFavelier commented Oct 21, 2020

The remaining vtkOpenGLActor is a true mystery.

It appears in this code:

import gc
import time
import numpy as np
import mne
rr, tris = mne.read_surface(mne.datasets.sample.data_path() + '/subjects/fsaverage/surf/lh.inflated')
tris = np.pad(tris, ((0, 0), (1, 0)), 'constant')
assert tris.shape[1] == 4
tris[:, 0] = 3
for _ in range(1):
    brain = mne.viz.Brain('fsaverage', 'both', 'inflated')
    brain.close()
    del brain
    time.sleep(0.1)
    gc.collect()
    print([o.__class__.__name__ for o in gc.get_objects() if o.__class__.__name__.startswith('vtk')], len(gc.get_objects()))

But not in this one:

import gc
import time
import numpy as np
import pyvista
import mne
rr, tris = mne.read_surface(mne.datasets.sample.data_path() + '/subjects/fsaverage/surf/lh.inflated')
tris = np.pad(tris, ((0, 0), (1, 0)), 'constant')
assert tris.shape[1] == 4
tris[:, 0] = 3
for _ in range(1):
    brain = mne.viz.Brain('fsaverage', 'both', 'inflated')
    brain.close()
    del brain
    time.sleep(0.1)
    gc.collect()
    print([o.__class__.__name__ for o in gc.get_objects() if o.__class__.__name__.startswith('vtk')], len(gc.get_objects()))

@GuillaumeFavelier
Copy link
Contributor Author

@larsoner on my machine, using this script:

import gc
import time
import numpy as np
import mne
rr, tris = mne.read_surface(mne.datasets.sample.data_path() + '/subjects/fsaverage/surf/lh.inflated')
tris = np.pad(tris, ((0, 0), (1, 0)), 'constant')
assert tris.shape[1] == 4
tris[:, 0] = 3
for _ in range(1):
    brain = mne.viz.Brain('fsaverage', 'both', 'inflated')
    brain.close()
    del brain
    time.sleep(0.1)
    gc.collect()
    print([o.__class__.__name__ for o in gc.get_objects() if o.__class__.__name__.startswith('vtk')], len(gc.get_objects()))

gives:

['vtkOpenGLActor']

And strangely enough if I import pyvista, it disappears. I don't have an explanation for now.

Can you confirm that you obtain a similar list?

@larsoner larsoner closed this Oct 21, 2020
@larsoner
Copy link
Member

@GuillaumeFavelier I incorporated this into #8379 except for d597e80. Feel free to cherry-pick that one and push to my PR. Travis should fail there soon with some VTK objects linger errors, you should be able to replicate locally on my branch with just:

pytest mne/viz/_brain -x

@GuillaumeFavelier
Copy link
Contributor Author

GuillaumeFavelier commented Oct 22, 2020

I'll work directly on your branch then 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants