Skip to content
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

Redo the ImageSetData class to use less memory #438

Merged
merged 14 commits into from Jan 14, 2022
Merged

Redo the ImageSetData class to use less memory #438

merged 14 commits into from Jan 14, 2022

Conversation

phyy-nx
Copy link

@phyy-nx phyy-nx commented Sep 25, 2021

This change alters ImageSetData to keep its models in an array of len(indices) instead of len(max(indices)). Concurrently, whenever the imageset dereferences a model, it needs to not dereference the indices array. Retain dereferencing the indices array when reading the data.

Mostly untested. Needs review and likely further work. Work in progress.

Closes #437

@phyy-nx
Copy link
Author

phyy-nx commented Sep 25, 2021

Note, as part of working on this changeset I created a combined experiment list with 10 experiments drawn from a single SwissFEL data file with over a thousand images. Normally, the combined experiment list would have 10 imagesets in it and be very slow to load. Compared to the single image case with takes 20 seconds, this takes 2 minutes to load in the image viewer.

I then made a version with 10 experiments and a single imageset, where that imageset had single_file_indices set to an array of 10 (disjoint) image indices. Indeed, it only took 20 seconds to load. It's definitely where we need to go for processing stills.

@phyy-nx
Copy link
Author

phyy-nx commented Sep 29, 2021

Alternative version on branch sfi_redo2 using maps for the ImageSetData model arrays. Also not working yet.

@dwpaley
Copy link

dwpaley commented Nov 4, 2021

Looking into these test failures. Here's a simple snippet. (must run pytest --regression $MODULES/dxtbx/tests/test_imageset.py::test_single_file_indices first to get the test image)

$ cd $BUILD/dials_data/image_examples
$ cat > repro.py
import dxbtx.format
fname = 'SACLA-MPCCD-run266702-0-subset.h5'
format_class=dxtbx.format.Registry.get_format_class_for_file(fname)
iset = format_class.get_imageset([fname], single_file_indices=None, lazy=False)

$ libtbx.python repro.py
Traceback (most recent call last):
  File "repro.py", line 4, in <module>
    iset = format_class.get_imageset([fname], single_file_indices=None, lazy=False)
  File "/dev/shm/dwpaley/20211104/modules/dxtbx/src/dxtbx/format/FormatMultiImageLazy.py", line 42, in get_imageset
    lazy=lazy,
  File "/dev/shm/dwpaley/20211104/modules/dxtbx/src/dxtbx/format/FormatMultiImage.py", line 240, in get_imageset
    reader=reader, masker=None, vendor=vendor, params=params, format=cls
Boost.Python.ArgumentError: Python argument types in
    ImageSetData.__init__(ImageSetData)
did not match C++ signature:
    __init__(boost::python::api::object, boost::python::api::object reader, boost::python::api::object masker, std::string template='', std::string vendor='', boost::python::dict params=None, boost::python::api::object format=None)
    __init__(boost::python::api::object, boost::python::api::object reader, boost::python::api::object masker)

@phyy-nx, just checking, can you confirm this is supposed to still work?

This change alters ImageSetData to keep its models in an array of len(indices) instead of len(max(indices)).  Concurrently, whenever the imageset dereferences a model, it needs to not dereference the indices array.  Retain dereferencing the indices array when reading the data.
Needed to make sliced image sets when the list of models held is only as long as the number of indices.
@codecov
Copy link

codecov bot commented Nov 9, 2021

Codecov Report

Merging #438 (f7f4cca) into main (759ac47) will increase coverage by 0.01%.
The diff coverage is 76.66%.

❗ Current head f7f4cca differs from pull request most recent head d6595d4. Consider uploading reports for the commit d6595d4 to get more accurate results

@@            Coverage Diff             @@
##             main     #438      +/-   ##
==========================================
+ Coverage   66.20%   66.22%   +0.01%     
==========================================
  Files         185      185              
  Lines       16792    16813      +21     
  Branches     2375     2382       +7     
==========================================
+ Hits        11117    11134      +17     
- Misses       5110     5111       +1     
- Partials      565      568       +3     

@dwpaley
Copy link

dwpaley commented Nov 16, 2021

I've done some testing of memory usage by loading one of three expt files which contain 1, ~25 and ~400 single-image imagesets and comparing heap usage before/after.

Main branch:
1 expt: 28 kB
25 expts: 5.5 MB
400 expts: 58.5 MB

This branch:
1 expt: 14 kB
25 expts: 312 kB
400 expts: 4.2 MB

On the main branch, for each imageset, four large-ish chunks are allocated on the heap. It looks like this:

 0x000055bb02d16670 -> 0x000055bb02d2b4df    85616 bytes uncategorized::85616 bytes |....................|
    0x000055bb02d2b4e0 -> 0x000055bb02d4034f    85616 bytes uncategorized::85616 bytes |....................|
    0x000055bb02d40350 -> 0x000055bb02d551bf    85616 bytes uncategorized::85616 bytes |....................|
    0x000055bb02d551c0 -> 0x000055bb02d6a02f    85616 bytes uncategorized::85616 bytes |....................|
    0x000055bb02d6a030 -> 0x000055bb02d6b51f     5360 bytes uncategorized::5360 bytes |....................|
    0x000055bb02d6b520 -> 0x000055bb02d6b55f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6b560 -> 0x000055bb02d6b59f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6b5a0 -> 0x000055bb02d6b5df       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6b5e0 -> 0x000055bb02d6b61f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6b620 -> 0x000055bb02d6b65f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6b660 -> 0x000055bb02d6b68f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02d6b690 -> 0x000055bb02d6b6cf       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6b6d0 -> 0x000055bb02d6b6ef       32 bytes uncategorized::32 bytes |...........I.�.. ...|
    0x000055bb02d6b6f0 -> 0x000055bb02d6b72f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6b730 -> 0x000055bb02d6b76f       64 bytes uncategorized::64 bytes |..cdxtbx.format.Form|
    0x000055bb02d6b770 -> 0x000055bb02d6b99f      560 bytes uncategorized::560 bytes |`c.?.�....>6.�......|
    0x000055bb02d6b9a0 -> 0x000055bb02d6b9df       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6b9e0 -> 0x000055bb02d6ba1f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6ba20 -> 0x000055bb02d6ba3f       32 bytes uncategorized::32 bytes |.a.?.�..........p...|
    0x000055bb02d6ba40 -> 0x000055bb02d6ba7f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6ba80 -> 0x000055bb02d6babf       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6bac0 -> 0x000055bb02d6baff       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02d6bb00 -> 0x000055bb02d6bb2f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02d6bb30 -> 0x000055bb02d6bb5f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02d6bb60 -> 0x000055bb02d6bb8f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02d6bb90 -> 0x000055bb02d6bbbf       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02d6bbc0 -> 0x000055bb02d6bbef       48 bytes   C:string data:None |1d5cb002-249a-4a84-a|
    0x000055bb02d6bbf0 -> 0x000055bb02d6bc1f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02d6bc20 -> 0x000055bb02d6bc4f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02d6bc50 -> 0x000055bb02d6bc7f       48 bytes   C:string data:None |bd770f1e-d5b6-4461-a|
    0x000055bb02d6bc80 -> 0x000055bb02d6bcaf       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02d6bcb0 -> 0x000055bb02d81cff    90192 bytes uncategorized::90192 bytes |....................|
    0x000055bb02d81d00 -> 0x000055bb02d97d4f    90192 bytes uncategorized::90192 bytes |....................|
    0x000055bb02d97d50 -> 0x000055bb02dadd9f    90192 bytes uncategorized::90192 bytes |....................|
    0x000055bb02dadda0 -> 0x000055bb02dc3def    90192 bytes uncategorized::90192 bytes |....................|
    0x000055bb02dc3df0 -> 0x000055bb02dc53ff     5648 bytes uncategorized::5648 bytes |....................|
    0x000055bb02dc5400 -> 0x000055bb02dc543f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc5440 -> 0x000055bb02dc547f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc5480 -> 0x000055bb02dc54bf       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc54c0 -> 0x000055bb02dc54ff       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc5500 -> 0x000055bb02dc553f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc5540 -> 0x000055bb02dc556f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02dc5570 -> 0x000055bb02dc55af       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc55b0 -> 0x000055bb02dc55cf       32 bytes uncategorized::32 bytes |...........I.�.. ...|
    0x000055bb02dc55d0 -> 0x000055bb02dc560f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc5610 -> 0x000055bb02dc564f       64 bytes uncategorized::64 bytes |..cdxtbx.format.Form|
    0x000055bb02dc5650 -> 0x000055bb02dc587f      560 bytes uncategorized::560 bytes |`c.?.�....>6.�......|
    0x000055bb02dc5880 -> 0x000055bb02dc58bf       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc58c0 -> 0x000055bb02dc58ff       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc5900 -> 0x000055bb02dc591f       32 bytes uncategorized::32 bytes |.a.?.�..........PV..|
    0x000055bb02dc5920 -> 0x000055bb02dc595f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc5960 -> 0x000055bb02dc599f       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc59a0 -> 0x000055bb02dc59df       64 bytes uncategorized::64 bytes |.H.G.�..............|
    0x000055bb02dc59e0 -> 0x000055bb02dc5a0f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02dc5a10 -> 0x000055bb02dc5a3f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02dc5a40 -> 0x000055bb02dc5a6f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02dc5a70 -> 0x000055bb02dc5a9f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02dc5aa0 -> 0x000055bb02dc5acf       48 bytes   C:string data:None |8d3076c5-03c8-43fc-b|
    0x000055bb02dc5ad0 -> 0x000055bb02dc5aff       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02dc5b00 -> 0x000055bb02dc5b2f       48 bytes uncategorized::48 bytes |...G.�..............|
    0x000055bb02dc5b30 -> 0x000055bb02dc5b5f       48 bytes   C:string data:None |4c69c301-71e7-4d55-b|
    0x000055bb02dc5b60 -> 0x000055bb02dc5b8f       48 bytes uncategorized::48 bytes |...G.�..............|

Reading down the heap addresses in order, the sizes of the 4 large objects grow up to ~100 kB, then reset to a few kB, then grow again. I can't see specifically what is there, but it becomes quite a lot. This is no longer observed with this PR.

Would welcome any other suggestions for assessing the memory usage, but I hope it's clear enough that the improvement here is pretty big.

dxtbx and xfel_regression tests all pass. @graeme-winter, any suggestion for who could review this?

@dwpaley dwpaley marked this pull request as ready for review November 17, 2021 14:48
@dwpaley
Copy link

dwpaley commented Nov 17, 2021

The four large objects are these ones allocated in the ImageSetData constructor, I believe @phyy-nx already appreciated this through an unusual technique of "understanding the code" :)

class ImageSetData {
[...]
ImageSetData(boost::python::object reader, masker_ptr masker)
    : reader_(reader),
      masker_(masker),
      beams_(boost::python::len(reader)),
      detectors_(boost::python::len(reader)),
      goniometers_(boost::python::len(reader)),
      scans_(boost::python::len(reader)),
      reject_(boost::python::len(reader)) {}
[...]
scitbx::af::shared<beam_ptr> beams_;
scitbx::af::shared<detector_ptr> detectors_;
scitbx::af::shared<goniometer_ptr> goniometers_;
scitbx::af::shared<scan_ptr> scans_;
[...]
}

@dwpaley
Copy link

dwpaley commented Nov 17, 2021

Note that the large chunks allocated on the heap are each 16 bytes x the length of the Reader that was used to construct the ImageSetData. Thus a chunk of size 78592 corresponds to an af::shared<boost::shared_ptr<BeamBase> > with 4911 entries, and therefore came from an experiment with single_file_indices [4910]. The shared_ptr just contains two 64-bit pointers as described here: https://en.cppreference.com/w/cpp/memory/shared_ptr, but it adds up because we're unnecessarily storing many thousands of them.

@ndevenish
Copy link
Collaborator

This was agreed to be merged at the 2021-12-09 DIALS core meeting. However, I'm reluctant to merge this in right before a release, so shall merge this once 3.8 is out.

@ndevenish
Copy link
Collaborator

Since this is quite a significant change, I realised @dwpaley that you weren't in the AUTHORS list in dxtbx, so I added it. Please let me know if this isn't okay.

Otherwise, I intend to merge this tomorrow morning.

@ndevenish ndevenish merged commit 9047e1c into main Jan 14, 2022
@ndevenish ndevenish deleted the sfi_redo branch January 14, 2022 09:21
@dwpaley
Copy link

dwpaley commented Jan 14, 2022

Not a problem of course :) Thanks!

dwpaley added a commit that referenced this pull request Feb 16, 2022
When image files are numbered from 0, the resulting ImageSequence has batch_offset -1. This situation was handled incorrectly after #438; this fixes the bug and adds a test.

Closes dials/dials#2011.
phyy-nx added a commit to dials/dials that referenced this pull request Jun 1, 2022
Two specific fixes:
- In _determine_max_memory_needed, check if the scan is a still, in which case its bbox z coordinates should be 0, 1
- In dials.import, in the manual geometry updater, fix convert_sequences_to_stills. This was likely fallout from cctbx/dxtbx#438, where the imagset indices were redone to use less memory.

Fixes #2127
phyy-nx added a commit to dials/dials that referenced this pull request Oct 7, 2022
* Fix raster scans for dials.stills_process

Two specific fixes:
- In _determine_max_memory_needed, check if the scan is a still, in which case its bbox z coordinates should be 0, 1
- In dials.import, in the manual geometry updater, fix convert_sequences_to_stills. This was likely fallout from cctbx/dxtbx#438, where the imageset indices were redone to use less memory.

Fixes #2127

Co-authored-by: James Beilsten-Edmands <jbeilstenedmands@gmail.com>
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.

Sliced ImageSets use too much memory
4 participants