Netflix/RxJava — pull request #651 by benjchristensen (2013-12-22 07:10:35+00:00)
Title: Subjects Refactor - Non-Blocking, Common Abstraction, Performance

These changes evolved out of reviewing pull request https://github.com/Netflix/RxJava/pull/605 and code reviewing master branch with @headinthebox and validating functionality against .Net. I also spent some time to figure out a non-blocking design that I believe is working.

The results for `ReplaySubject` is that it performs faster than what is currently in master and does not block in almost all cases. I have not yet perf-tested the other 3 Subjects.

- Common logic composed inside SubjectSubscriptionManager used by all 4 Subjects
- ReplaySubject does not block while replaying to new subscribers
- Added unit tests and fixed behavior while reviewing with @headinthebox compared to Rx.Net
- Uses mostly non-blocking approach (I believe it’s all correct, unit and long running tests have been used to prove it. The tests found concurrency problems during development and became stable once I got the design correct. As with all concurrent code I may be missing something.)
- The only place it blocks is if a new Observer subscribes, completes replay and `onComplete`/`onError` are in process. It will wait for those to complete before applying the terminal state on itself.

As for performance, for relative comparison my machine gets these numbers for the master branch:

```
* ArrayList with raw values & synchronized access
*
* Run: 10 - 11,993,341 ops/sec
* Run: 11 - 11,719,523 ops/sec
* Run: 12 - 11,965,214 ops/sec
* Run: 13 - 11,814,730 ops/sec
* Run: 14 - 11,947,459 ops/sec
```

This commit gets these numbers:

```
* ArrayList with raw values & non-blocking (no synchronization)
*
* Run: 10 - 16,069,678 ops/sec
* Run: 11 - 15,954,688 ops/sec
* Run: 12 - 16,158,874 ops/sec
* Run: 13 - 16,209,504 ops/sec
* Run: 14 - 16,151,174 ops/sec
```

I'm curious about adding bounded support and using a circular-array rather than ArrayList and seeing if it performs better.

The performance tests are inside `SubjectPerformanceTests` and inspired by tests done in the non-blocking code workshop with Martin Thompson.

Some of the code is not as elegant as I'd like but was done for performance reasons. For example, I had a much more elegant version using a simple linked list but it had horrible performance. Another example is that using `Notification` to wrap every `onNext` so we have a single data structure is very inefficient so it maintains `T` without wrapping and then conditional logic to check for terminal state. These and other things have been determined while doing perf tests during development and I have consciously moved towards performance rather than the most elegant code.

I'd appreciate feedback on concurrency problems if any exist or ways of getting more performance out while achieving the same functional requirements.