Skip to content

1.x: reduce Subscriber's creation overhead #3479

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

Closed
wants to merge 2 commits into from

Conversation

akarnokd
Copy link
Member

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)

image

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 test Subscribers before subscription thus forcing a heap allocation in each case.

Note that Subscriber has now more synchronization which combined with the synchronization in SubscriptionList may increase the overhead elsewhere; I plan to run more benchmarks in the morning. The solution would be to inline the logic of SubscriptionList into Subscriber directly.

Note also the recursive call to add and unsubscribe in case the Subscriber was created in sharing mode. In RxJava 1.x, operators like to share a single underlying SubscriptionList 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 a Subscriber, especially in async operators, whose overhead may be shadowed by other things and thus not really a problem.

@akarnokd akarnokd added this to the 1.0.x milestone Oct 28, 2015
akarnokd added a commit to akarnokd/RxJava that referenced this pull request Oct 29, 2015
Two of the tests used the wrong subscriber.

Added a benchmark which should help verify the overhead of checking
isUnsubscribed within range in ReactiveX#3479 because I suspect that will get
worse there.
@akarnokd
Copy link
Member Author

I've updated the SubscribingPerf to see what effect the isUnsubscribed implementation has and its not good: the throughput is generally reduced by ~20% due to the additional indirections:

Benchmark comparison (i7 4790, Windows 7 x64, Java 8u66):

image

I've run out of ideas; due to the structure of the 1.x classes and the mandatory resource management of Subscriber I don't believe subscribing can be improved any further without endangering the streaming performance.

@akarnokd akarnokd closed this Oct 29, 2015
tejasgarde pushed a commit to tejasgarde/RxJava that referenced this pull request Apr 4, 2016
Two of the tests used the wrong subscriber.

Added a benchmark which should help verify the overhead of checking
isUnsubscribed within range in ReactiveX#3479 because I suspect that will get
worse there.
@akarnokd akarnokd deleted the SubscribingPerfFix1x branch May 18, 2016 22:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant