-
Notifications
You must be signed in to change notification settings - Fork 262
Very slow slicing with indexed_gzip #558
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
Comments
How long does it take if |
|
I hate to ask, but what happens of you run the one without |
New session: In [1]: import nibabel as nib
In [2]: nib.openers.HAVE_INDEXED_GZIP=False
In [3]: img = nib.load('/data/bids/ds000114/sub-07/ses-test/anat/sub-07_ses-test_T1w.nii.gz')
In [4]: %time img.dataobj[0, :, :]
CPU times: user 420 ms, sys: 4 ms, total: 424 ms
Wall time: 424 ms
Out[4]:
array([[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
...,
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.]], dtype=float32) |
Does the keep file open flag make a difference here? I thought that call would result in a read with a single open file-handle anyway? Is it possible that indexed gzip is just much slower doing these types of read than normal gzip? |
The first version I showed was with In [1]: import nibabel as nib
In [2]: nib.arrayproxy.KEEP_FILE_OPEN_DEFAULT
Out[2]: False
In [3]: img = nib.load('/data/bids/ds000114/sub-07/ses-test/anat/sub-07_ses-test_T1w.nii.gz')
In [4]: %time img.dataobj[0]
CPU times: user 3min 24s, sys: 3.89 s, total: 3min 28s
Wall time: 3min 28s
Out[4]:
array([[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
...,
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.]], dtype=float32) In [1]: import nibabel as nib
In [2]: nib.arrayproxy.KEEP_FILE_OPEN_DEFAULT
Out[2]: False
In [3]: nib.arrayproxy.KEEP_FILE_OPEN_DEFAULT='auto'
In [4]: img = nib.load('/data/bids/ds000114/sub-07/ses-test/anat/sub-07_ses-test_T1w.nii.gz')
In [5]: %time img.dataobj[0]
CPU times: user 3min 26s, sys: 3.81 s, total: 3min 30s
Wall time: 3min 30s
Out[5]:
array([[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
...,
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.]], dtype=float32) |
Right - and I guess you get the same result for |
Yes and yes. I don't know the internals of why it's that much slower, but we're talking a 500x slowdown on a slice operation that happens by default when you upgrade to master. I'm tempted to revert for the coming release, and we can try to do some benchmarking and figure out where this penalty is coming from. |
@pauldmccarthy - any thoughts here? Is this slowdown expected? I'm sure you know, but for my own benefit, the slice that Chris is doing will read byte 0 then skip X-1 bytes (where X is the length of the first axis) and read another byte, then skip another X-1 bytes, and so on, doing this a total of Y * Z times (where Y and Z are the lengths of the second and third axes). |
Hey guys, sorry for the delay - I just got back from holiday. I can reproduce this - As I see it, the main problem here is that if I will open a new PR such that In the meantime, I'll do some debugging to see if I can get any speed up in |
In addition to disabling A reasonable minimum might be a 256^3 volume of float32's (64MiB + <1KiB header) as that's about the largest structural image you're likely to get. I'd suggest 2 or 4 times that to try to get a reasonable block of functional volumes, but that might be too large once you start dealing with many files, unless you have a library-wide cache. |
Update: Wrapping the |
Paul - great! - well done. Any chance of putting the benchmarks into the PR so we can run them from time to time? |
@matthew-brett great idea - I will add some unit tests into #562 as well :) |
I get promising results using the benchmark script below - times from 100 iterations of slicing
This is running against the running against my #562 branch of import time
import hashlib
import nibabel as nib
filename = 'sub-07_ses-test_T1w.nii.gz'
# (label for summary, keep_file_open, sliceobj)
tests = [
('gzip', False, (0, )),
('indexed_gzip', True, (0, )),
('indexed_gzip', 'auto', (0, )),
('gzip', False, (slice(None), slice(None), 0)),
('indexed_gzip', True, (slice(None), slice(None), 0)),
('indexed_gzip', 'auto', (slice(None), slice(None), 0)),
]
for label, keep_open, sliceobj in tests:
niters = 100
start = time.time()
expectHash = None
for i in range(niters):
hashobj = hashlib.md5()
image = nib.load(filename, keep_file_open=keep_open)
hashobj.update(image.dataobj[0].ravel())
if expectHash is None:
expectHash = hashobj.hexdigest()
elif hashobj.hexdigest() != expectHash:
print('WARNING: {} / {} failed hash check'.format(label, i))
end = time.time()
elapsed = end - start
elapsed /= niters
print('{} (keep_file_open={}, slice={}): {} minutes, '
'{:0.4f} seconds (niters: {}, hash: {})'.format(
label,
keep_open,
sliceobj,
int(elapsed / 60),
elapsed % 60.0,
niters,
expectHash)) |
Those numbers look great. Thanks a lot for this, @pauldmccarthy! I assume you're also benchmarking on large time series, to make sure that we're not regressing in terms of time or memory, there? |
I've released a new version of |
To reproduce, you can test on an OpenFMRI dataset:
datalad install -r ///openfmri/ds000114 cd ds000114 git annex get sub-07/ses-test/anat/sub-07_ses-test_T1w.nii.gz
Install
indexed_gzip
:I'm not sure how long it takes. Slicing along the last dimension is fast, so it's not reading the file that's the issue.
I think the fix will be to disable
indexed_gzip
ifnibabel.arrayproxy.KEEP_FILE_OPEN_DEFAULT is False
.The text was updated successfully, but these errors were encountered: