Skip to content

[WIP] Lightweight implementation of InterfaceResult #2805

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

Closed
wants to merge 11 commits into from

Conversation

oesteban
Copy link
Contributor

Summary

(Ref #2776)
This PR replaces the good old Bunch. Bunch has been moved to the module nipype.utils.misc, although it is still loadable from nipype.interfaces.base.

The memory fingerprint of runtime objects improve by some 20%, not that we will see a huge difference but it justifies itself as an optimization:

>>> from nipype.interfaces.base.support import InterfaceRuntime 
>>> from pympler import asizeof 
>>> rt = InterfaceRuntime(cmdline='/bin/echo', cwd='/home/oesteban', stdout='some text that is actually somewhat longer than other parameters') 
>>> asizeof.asizeof(rt)
472

>>> from nipype.interfaces.base import Bunch
>>> from pympler import asizeof
>>> rt = Bunch(cmdline='/bin/echo', cwd='/home/oesteban', stdout='some text that is actually somewhat longer than other parameters')
>>> asizeof.asizeof(rt)
584

** pympler measures the whole size of the data structure, including the full tree of references.

List of changes proposed in this PR (pull-request)

  • A new nipype.interfaces.base.support.InterfaceRuntime object with a reduce memory footprint that the previous Bunch.
  • Bunch has been moved to nipype.utils.misc and some unused code has been removed.

Acknowledgment

  • (Mandatory) I acknowledge that this contribution will be available under the Apache 2 license.

This PR replaces the good old ``Bunch``. ``Bunch`` has been moved to the
module ``nipype.utils.misc``, although it is still loadable from
``nipype.interfaces.base``.
@oesteban
Copy link
Contributor Author

oesteban commented Nov 27, 2018

One caveat is that this is not better on Python 2.7:

>>> from nipype.interfaces.base.support import InterfaceRuntime
>>> from nipype.interfaces.base import Bunch
>>> from pympler import asizeof 
>>> rt = InterfaceRuntime(cmdline='/bin/echo', cwd='/home/oesteban', stdout='some text that is actually somewhat longer than other parameters') 
>>> asizeof.asizeof(rt)
736

>>> rt2 = Bunch(cmdline='/bin/echo', cwd='/home/oesteban', stdout='some text that is actually somewhat longer than other parameters')
>>> asizeof.asizeof(rt2)
688

The difference is about 10% (half as much as before), and we can always patch the InterfaceRuntime object to be a Bunch for Python 2.

EDIT: the difference goes away with bigger objects:

>>> from nipype.interfaces.base.support import InterfaceRuntime
>>> from nipype.interfaces.base import Bunch
>>> from pympler import asizeof 
>>> rt = InterfaceRuntime(cmdline='/bin/echo', cwd='/home/oesteban', stdout='some text that is actually somewhat longer than other parameters', stderr='onemore')
>>> asizeof.asizeof(rt)
784

>>> rt2 = Bunch(cmdline='/bin/echo', cwd='/home/oesteban', stdout='some text that is actually somewhat longer than other parameters', stderr='onemore')
>>> asizeof.asizeof(rt2)
784

and

>>> from nipype.interfaces.base.support import InterfaceRuntime
>>> from nipype.interfaces.base import Bunch
>>> from pympler import asizeof 
>>> rt = InterfaceRuntime(cmdline='/bin/echo', cwd='/home/oesteban', stdout='some text that is actually somewhat longer than other parameters', stderr='onemore', returncode=0)
>>> asizeof.asizeof(rt)
808

>>> rt2 = Bunch(cmdline='/bin/echo', cwd='/home/oesteban', stdout='some text that is actually somewhat longer than other parameters', stderr='onemore', returncode=0)
>>> asizeof.asizeof(rt2)
856

And, in python 3.7, things start to be very different:

>>> from nipype.interfaces.base.support import InterfaceRuntime
>>> from nipype.interfaces.base import Bunch
>>> from pympler import asizeof 
>>> rt = InterfaceRuntime(cmdline='/bin/echo', cwd='/home/oesteban', stdout='some text that is actually somewhat longer than other parameters', stderr='onemore', returncode=0)
>>> asizeof.asizeof(rt)
552

>>> rt2 = Bunch(cmdline='/bin/echo', cwd='/home/oesteban', stdout='some text that is actually somewhat longer than other parameters', stderr='onemore', returncode=0)
>>> asizeof.asizeof(rt2)
784

@codecov-io
Copy link

codecov-io commented Nov 27, 2018

Codecov Report

Merging #2805 into master will decrease coverage by 3.38%.
The diff coverage is 79.03%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2805      +/-   ##
==========================================
- Coverage   67.52%   64.14%   -3.39%     
==========================================
  Files         341      340       -1     
  Lines       43312    43274      -38     
  Branches     5371     5369       -2     
==========================================
- Hits        29248    27758    -1490     
- Misses      13363    14444    +1081     
- Partials      701     1072     +371
Flag Coverage Δ
#smoketests ?
#unittests 64.14% <79.03%> (-0.81%) ⬇️
Impacted Files Coverage Δ
nipype/utils/draw_gantt_chart.py 13.07% <0%> (+0.25%) ⬆️
nipype/workflows/smri/freesurfer/autorecon3.py 68.4% <0%> (-6.95%) ⬇️
nipype/algorithms/modelgen.py 58.93% <100%> (-8.51%) ⬇️
nipype/interfaces/spm/model.py 43.09% <100%> (+0.13%) ⬆️
nipype/interfaces/base/__init__.py 100% <100%> (ø) ⬆️
nipype/interfaces/base/core.py 85% <100%> (-2.09%) ⬇️
nipype/pipeline/engine/utils.py 71.46% <20%> (-10.69%) ⬇️
nipype/pipeline/engine/nodes.py 77.9% <50%> (-6.62%) ⬇️
nipype/utils/bunch.py 73.46% <73.46%> (ø)
nipype/interfaces/base/support.py 96.82% <96.07%> (+25.82%) ⬆️
... and 52 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0fde422...95444f2. Read the comment docs.

@oesteban oesteban added this to the 1.1.7 milestone Nov 27, 2018
@oesteban oesteban changed the title [ENH] Lean implementation of InterfaceResult [ENH] Lightweight implementation of InterfaceResult Nov 27, 2018
>>> pickleds = pickle.dumps(rt)
>>> newrt = pickle.loads(pickleds)
>>> newrt
Bunch(cmdline='/bin/echo', cwd='/scratch/workflow', returncode=0)
Copy link
Member

Choose a reason for hiding this comment

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

Why Bunch and not InterfaceRuntime?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is for backwards compatibility. If I change Bunch I'm afraid I'll need to update all the tests. But I can make that extra mile.

Copy link
Member

Choose a reason for hiding this comment

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

I would much prefer repr to accurately reflect the class name. Sorry that it's a lot of work...

"""
__slots__ = ['interface', 'runtime', 'inputs', 'outputs', 'provenance']
version = __version__
Copy link
Member

Choose a reason for hiding this comment

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

I think the goal of the version attribute was to invalidate results loaded from an incompatible format, not record the nipype version.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

MMM so this is why we had 2.0 here? was that the pickle protocol version?

Copy link
Member

Choose a reason for hiding this comment

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

@satra Perhaps you can clarify? I interpreted as the InterfaceResult API version.

def __setstate__(self, state):
"""Necessary for un-pickling"""
for key in self.__class__.__slots__:
setattr(self, key, state.get(key, None))
Copy link
Member

Choose a reason for hiding this comment

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

Should you check that state.get('version') == self.version?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Throw a warning at least would be appropriate. Since I was trying to do a minimal proof of concept, I passed. But yes, I'll include the warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Warning added 👍

@oesteban oesteban force-pushed the enh/leaner-runtime-object branch from 78fed19 to dd6351a Compare November 28, 2018 23:47
@oesteban oesteban changed the title [ENH] Lightweight implementation of InterfaceResult [WIP] Lightweight implementation of InterfaceResult Nov 29, 2018
@oesteban
Copy link
Contributor Author

Do not merge yet, still under testing.

@effigies effigies modified the milestones: 1.1.7, 1.1.8 Dec 14, 2018
@effigies
Copy link
Member

Hi, just a reminder that the 1.1.8 release is targeted for January 28. Please let us know if you'd like to try to finish this up for that release.

@oesteban
Copy link
Contributor Author

We can push this one to 1.1.9

@effigies effigies modified the milestones: 1.1.8, 1.1.9 Jan 16, 2019
@effigies
Copy link
Member

@oesteban Just a bump to note that the merge window closes next Friday, Feb 22.

@oesteban
Copy link
Contributor Author

I'm going to close this one for now. I am confident this is a good direction, but I need to mull over the details of the implementation. I'll reopen when ready.

@oesteban oesteban closed this Feb 14, 2019
@effigies
Copy link
Member

I'm going to mark it orphaned. Let's start a new PR when you're ready.

@effigies
Copy link
Member

This pull request is "orphaned," which means it has been deemed to be abandoned by its original author. Orphaned pull requests have not been rejected, and we hope that if a user sees one that will meet their needs with a little work, that they will fork it and open a new pull request (or, in the case of the original author, reopen the original PR).

We ask that all adopted PRs be updated to merge or rebase the current master. If you would like to adopt a PR and need help getting started, any of a number of contributors will be happy to help.

@effigies effigies removed this from the 1.1.9 milestone Feb 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants