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
poor performance in OmmConsumerImpl.unregister(handle) #247
Comments
@dlipofsky Thank you for your performance improvement suggestions! We will look into them as to what extent they might affect the logic and make improvements in one of the future releases. |
More data: a single call to |
@dlipofsky Thanks for more information! |
I was able to compile a few alternatives and test it myself. The ArrayList solution won't work everywhere. It works for I also noticed some of these lists are empty in my use case
And 269611 is a significant difference from the 356278 open handles I have (these are RICs I registered and did not receive a StreamState CLOSED message). I noticed a lot of pooling. Pooling is generally a bad idea (especially since Java 1.5) unless the objects are expensive to create. Modern generational GC expects mosts objects to be short lived, and so pooling intefers with it operating at its most efficient. You spend a lot of time adding and removing objects from the pool. It introduces the risk of a memory leak. You add risk of bugs if objects are not properly cleared, and add extra time to clear the objects. In a multi-threaded app you add a synchronization bottleneck and a risk of deadlock, or a race condition if not properly synchronized. It's better to let Java manage the memory for you. It would probably be much faster if you strip out all the pooling. |
@dlipofsky Thank you for more detailed analysis and suggestions! We will consider them in our own investigation. Regarding pooling, the objects that are pooled, say, on the ValueAdd level turn out to be expensive to create on the fly indeed. |
I see for example the pooling of LinkedList (line 88 of WlItemHandler: the outer LinkedList is the pool and the inner one is objects being pooled)
LinkedList is definitely not something that should be pooled. I see pooling on and on StatusMsg on line 110; this the the one that attracted my attention in the profiler as using significant time adding it back to the pool
It looks like pooling was the default design choice throughout the SDK, and really it should be used only when profiling indicates it is necessary. |
This is included with tag, Real-Time-SDK-2.1.3.L1. Please let us know if you have further questions. |
@vlevendel , We are currently using |
@dlipofsky 2.1.3.L1 is the same as EMA/ETA 3.7.3. This information is found in the CHANGELOG files for each API Flavor: |
When large number of handles are registered then calls to
OmmConsumerImpl.unregister(handle)
become very slow.I traced this to$O(N)$ on a LinkedList (each time it is called it has to walk down the list to$O(N^{2})$ . If it finds a match it then calls
WlItemHandler.removeUserRequestFromClosedStream(WlRequest)
whichiterates over a
LinkedList
using an integer index, callingLinkedList.get(i)
for each index.Since
get(i)
isget to that index), just this step is
LinkedList.remove(i)
which again has to walk down the entire list to find that index and remove it.Calling this on every handle in a set of 379233 handles took 48 minutes!$O(N^{3})$
Doing the whole list makes it
99% of the CPU time is in
WlItemHandler.removeUserRequestFromClosedStream(WlRequest)
.If you use an iterator you can just walk the list just once, turning the method into$O(N)$ . The performance improvement will be enormous. Basically change this
to
or in the first case you could even just do
and let Java do the iterate and remove for you.
There are many other of examples of this same problem in this class, and they really all should be fixed.
However rather than making these code changes it might be better to consider a different Collection type. You can use$O(1)$ performance (constant time) for these operations and thus be even faster. If you really need to access by index or you do want duplicates you could use
LinkedHashSet
if want to preserve order and don't want duplicates (and I assume you never want duplicates of handles; thebreak
in the original list appears to assume no duplicates) or justHashSet
if you don't care about order (and it would have a better memory footprint than the linked version). Both would giveArrayList
- this would certainly be the safest and easiest change with minimal behavior difference, and you could probably fix the whole class with just a find-and-replace; the remove call for ArrayList would be slower than either HashSet implementation but still much faster than removing from a LinkedList by index (it's technically O(N) but it's a very fast O(N) because it uses arraycopy to shift the internal array; over all it would still be much better than LinkedList).ArrayList
will also have the best memory footprint of any of these. I'd recommendArrayList
;LinkedList
only makes sense if the dominate behavior is inserting to the middle or head of the list, or removing from the middle or head of the list, and you need to allow duplicates.The text was updated successfully, but these errors were encountered: