-
Notifications
You must be signed in to change notification settings - Fork 318
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
An alternative implementation to speed up iterating successive DcmList
elements using seek_to
#114
Conversation
…profiling suggested that some 50% of the time (*) was spent in `DcmPixelSequence` essentially iterating a long list using successive calls to `DcmList::seek_to` (which has a random-access-like interface, but internally _iterates_ over a doubly linked list) Solution: In `DcmList`, keep the current absolute position, essentially catering for O(1) access whenever going to the previous or the next element (NB: truly _random_ access is still O(n), though) (*): the other 50% was spent decoding jpeg tiles, which is legit use of time of course
This is an alternative implementation to address the problem reported in #113. |
// `seek_to` to essentially iterate over the sequence, for example. If so, then | ||
// let's start iterating from the current position. Often the position we want | ||
// is simply the next position (or maybe the previous one). Let's make those | ||
// use cases be O(1), and not O(n). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the beef of the PR. Basically the complexity (of the updated seek_to
method) should be O(min(m,n))), where m is the distance of the desired position from the current position (and n is the number of elements in the list). Note that in the relevant actual use case (DcmPixelSequence
), it seems that typically m = 1.
Thank you for your second approach to deal with this issue. I still wonder how the application accesses the items of the In any case, I agree that the performance of |
The application simply constructs a For what it's worth, the call stack basically looks like this (line numbers as of the version having tag
Maybe the main culprit is around here (note that |
I see, so the problem lies either in the ugly implementation of the DcmList::seek_to() method or in the ugly use of DcmList::getItem() in the DJCodecDecoder::decodeFrame() method. As I wrote in my previous comment, we will discuss this internally during our team meeting end of next week. Thanks again. |
Generally speaking I would say that However, I guess it's hard to remove |
Thank you. I think in the meanwhile we will proceed with a fork that uses either #113 or this #114 (because our client actually expects us to improve speed on this front – and as said, this is the hot spot currently, in addition to decoding jpeg tiles which of course is expected to consume CPU cycles... and as also said, reading a corresponding Aperio .svs file is much faster at the moment). So if we find any problems with whichever approach we choose, I will come back here and post our findings. If you guys later come up with another solution (such as optimizing Thanks again for your response! |
I think this bad design of DcmList is part of the DCMTK from the very beginning, i.e. for more than 30 years now, but it's never too late to fix a real problem.
I will keep you informed about the outcome of our discussion. |
Thank you for the update. This kind of information is really useful for our discussion end of next week. |
dcmdata/libsrc/dclist.cc
Outdated
@@ -155,6 +171,7 @@ DcmObject *DcmList::insert( DcmObject *obj, E_ListPos pos ) | |||
node->nextNode = currentNode; | |||
currentNode->prevNode = node; | |||
currentNode = node; | |||
currentAbsolutePosition++; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@reunanen Isn't this increment wrong as the new node gets the position of the current node?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Not sure what I was thinking. 😬
Added test criteria, and fixed the problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for confirming my observation and also for the updated regression test.
…item Fix inserting before current list item
@reunanen As announced earlier, today we've talked about your two PRs at our DCMTK team meeting. We agreed that PR #114 is the one to incorporate into the "testing" branch of the DCMTK. I'll take your proposed changed, make a few modification (to meet our mainly undocumented "style guides"), test it and the commit the changes. It will take a few days until the changes are visible in DCMTK's public git repository (because of the nightly builds on various platforms and additional integration tests that are performed on the "testing" branch). Thanks again for your helpful contribution to the DCMTK! |
Significantly improved the performance of the seek_to() method, in particular when iterating a very long list using the position of the items, e.g. a DcmPixelSequence with a huge number of instances of DcmPixelItem such as in Whole Slide Imaging (WSI) objects. Internally, the seek_to() method is, e.g., used in the insert(), remove() and getItem() methods of DcmSequenceOfItems and DcmPixelSequence as well as in the getElement() and remove() method of DcmItem. Thanks to GitHub user "reunanen" for the report and proposed patch. This closes GitHub pull request #114.
The required changes have been committed (see f58412b). |
Thanks! However, don't this and this and this potentially leak memory? Ok surely one could do something like this at the call site – but I'm afraid it may be a bit too easy to forget to do it?
For example, it doesn't look like On a related note, would it be a practical choice to change the |
(And yes I am aware that my original solution would have blatantly failed when inserting |
Yes, I agree that now there are potential memory leaks when the list is "full", which is not very likely but also not impossible. In general, the caller should take care of freeing the memory because he allocated it. Currently, the return value of Using You see, it would require more investigation and probably more changes in other classes. Of course, if you like, you could investigate this in more detail and come up with a proposal. We would really appreciate this. |
I agree that this is a good design principle, but I don't think |
OK, maybe not in general, but in case of error (at least for You have to understand that |
Yeah, I'm not criticizing, just trying to understand where we are and how we could (realistically) be in an even better place. As you wrote: "it's never too late to fix a real problem". :) |
Yes, that's totally true :-) |
Problem: Reading a real-world DICOM image was a bit slow, and simple profiling suggested that some 50% of the time (*) was spent in
DcmPixelSequence
essentially iterating a long list using successive calls toDcmList::seek_to
(which has a random-access-like interface, but internally iterates over a doubly linked list)Solution: In
DcmList
, keep the current absolute position, essentially catering for O(1) access whenever going to the previous or the next element (NB: truly random access is still O(n), though)(*): the other 50% was spent decoding jpeg tiles, which is legit use of time of course