Closed benjchristensen closed 10 years ago
This issue is related to #1204 where CompositeSubscription
was found to be a major problem for object allocation and the implementation was changed.
If anyone wants to offer alternative implementations please ensure that 2 requirements are met (obviously beyond passing all functional unit tests):
1) It performs better in the JMH performance tests than what's currently in use (see above for how to execute them)
2) It does not cause excessive object allocation. (This is not as easy to identity in benchmarking. The best tool I know of right now is Java Flight Recorder. It can be fast in the JMH test but fail the object allocation requirement, like the CompositeSubscription
state machine implementation.)
Here is a resource for us to explore: https://github.com/JCTools/JCTools/tree/master/jctools-core/src/main/java/org/jctools
There is my hybrid composite in #1145. As for the hang, my guess is the lazySet that may overwrite a terminal state indicator, but it seems its performance isn't good enough anyway.
A lot has been done and we now have rx.internal.util
and rx.internal.until.unsafe
for various optimized data structures.
Closing this out as this was a very generic issue and has served its purpose.
This is to document and explore blocking vs non-blocking solutions for a couple use cases, particularly the
SerializedObserver
andCompositeSubscription
which are hot code paths in most use of Rx.Thus far the
synchronized
blocking implementations have won and are currently being used. Despite usingsynchronized
, the locks are not held while emitting notifications, only for mutating internal data structures.A key consideration is that object allocations must be kept low. The atomic state machine pattern has been attempted on both of these, and is elegant, but was a massive performance problem with
CompositeSubscription
due to some valid edge use cases withmerge
that result in hundreds of subscriptions being added to the data structure viaCompositeSubscription.add
and each time performing a state transition with object allocation.Following are details on the use cases, the current implementation, alternates that have been attempted, and performance results from JMH tests.
The intent of this is to document what has been attempted thus far and seek improvements from anyone who can provide better solutions.
SerializedObserver
Use Case:
Serialize
onNext
/onError
/onCompleted
calls from multiple threads to be sequential, but without synchronizing and blocking threads.Current: https://github.com/Netflix/RxJava/blob/master/rxjava-core/src/main/java/rx/observers/SerializedObserver.java
Discussions:
Alternate implementations:
Performance tests
Current Code (locks)
MPSC Queue
It completely hung once at this point:
queue and counter
state machine
CompositeSubscription/SubscriptionList
Use Case:
A
Subscription
implementation that allows the following:Subscription
to be invoked when the parent is unsubscribedonNext
in a tight loopisUnsubscribed
to true and callunsubscribe
on all subscriptions added viaadd
Subscription
, for example,merge
andflatMap
which usesmerge
This means that the list of subscriptions is multiple writes, a single read at
unsubscribe
at the end. The booleanisUnsubscribed
is ready many times, modified once.Current CompositeSubscription: https://github.com/Netflix/RxJava/blob/master/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java Current ChainedSubscription: https://github.com/Netflix/RxJava/blob/master/rxjava-core/src/main/java/rx/subscriptions/ChainedSubscription.java |-> will be renamed
Discussions:
Alternate implementations:
add(Subscription)
such asObservable.merge(hundreds-of-observables)
Performance tests
Current Code (locks)
State Machine
MPSC Queue + AtomicInteger