1.x: reduce Subscriber's creation overhead #3479
Closed
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR changes the internals of the
Subscriber
class to reduce its creation overhead by deferring the creation of the SubscriptionList until it is actually needed.Benchmark (i7 4770K, Windows 7 x64, Java 8u66)
For no-backpressure sources, the throughput is now doubled for the safe case and improved by ~20% for the unsafe case. For a backpressuring source, the improvement is between 3-10% but the error ranges overlap.
There is one case where the the throughput halved for some reason. Since JITs are smart, my best guess is that generally the benchmark method gets stack-allocated instead of heap allocated, hence the very large amounts relative to a range(1, 1) benchmark (which tops at 24 MOps/s).
However, the justStart case is worse than the baseline. My guess is that the
this
looks like it escaped and thus a regular heap allocation is required. I'll test this theory in the morning by blackholing the testSubscriber
s before subscription thus forcing a heap allocation in each case.Note that
Subscriber
has now more synchronization which combined with the synchronization inSubscriptionList
may increase the overhead elsewhere; I plan to run more benchmarks in the morning. The solution would be to inline the logic ofSubscriptionList
intoSubscriber
directly.Note also the recursive call to
add
andunsubscribe
in case theSubscriber
was created in sharing mode. In RxJava 1.x, operators like to share a single underlyingSubscriptionList
and if the chain is very long, that may prevent some JIT optimizations due to stack dept. The upside is that generally only a few resources are added to aSubscriber
, especially in async operators, whose overhead may be shadowed by other things and thus not really a problem.