ReactiveX/RxJava — pull request #2189 by benjchristensen (2014-12-23 05:11:27+00:00)
Title: RxRingBuffer with Inline Release

Since previous tests have shown that pooling does still make a big difference, I'm trying to find a way to do pooling without the concurrency issue. I've tried with work-in-progress counters, ReadWrite locks and other variants and all of them impact performance far too much.

This one makes different trade-offs:

It will release automatically when a ...
- terminal event is emitted
- unsubscribe/release occurs and queue is empty
- unsubscribe/release has occurred and poll/peek occurs

This can result in a queue not being put back in the pool if an early unsubscribe occurs from a different thread and causes the poll/peek to not trigger the release. In this case GC will reclaim the pool so it still functions, it just misses the pooling optimization.

There is a commented out test of using finalize(). It works as a safety net for the edge case, but at the cost of increased GC time.

Here are performance numbers of the various tests, this one being "Inline Volatile":

```
Benchmark (size) Mode Samples 1.x Inline Volatile + finalize RW Lock WIP
r.o.OperatorMergePerf.merge1SyncStreamOfN 1 thrpt 5 4757888.048 5264594.520 4956256.323 5288310.755 5032942.628
r.o.OperatorMergePerf.merge1SyncStreamOfN 1000 thrpt 5 44877.618 42845.758 39209.439 25742.696 29025.955
r.o.OperatorMergePerf.merge1SyncStreamOfN 1000000 thrpt 5 42.366 40.979 37.036 24.769 27.260
r.o.OperatorMergePerf.mergeNAsyncStreamsOfN 1 thrpt 5 99981.127 99910.070 94307.080 103112.286 103176.289
r.o.OperatorMergePerf.mergeNAsyncStreamsOfN 1000 thrpt 5 4.675 4.620 4.670 4.374 4.313
r.o.OperatorMergePerf.mergeNSyncStreamsOf1 1 thrpt 5 4751265.583 4706441.452 4376983.062 4739418.557 4673633.614
r.o.OperatorMergePerf.mergeNSyncStreamsOf1 100 thrpt 5 458704.984 480075.261 427165.143 483313.588 476318.407
r.o.OperatorMergePerf.mergeNSyncStreamsOf1 1000 thrpt 5 42493.290 42178.254 39640.240 42728.480 42542.171
r.o.OperatorMergePerf.mergeNSyncStreamsOfN 1 thrpt 5 5406448.872 5375090.752 5066264.570 5628401.294 4974892.417
r.o.OperatorMergePerf.mergeNSyncStreamsOfN 1000 thrpt 5 44.528 40.990 41.106 24.974 28.212
r.o.OperatorMergePerf.mergeTwoAsyncStreamsOfN 1 thrpt 5 76898.222 72655.377 69748.305 78283.565 78987.646
r.o.OperatorMergePerf.mergeTwoAsyncStreamsOfN 1000 thrpt 5 3172.653 2955.854 3064.749 1858.361 2204.948
r.o.OperatorMergePerf.oneStreamOfNthatMergesIn1 1 thrpt 5 5157134.576 5163837.644 4846336.744 5290961.536 5139893.848
r.o.OperatorMergePerf.oneStreamOfNthatMergesIn1 1000 thrpt 5 39961.491 39341.526 37312.117 40418.492 39163.267
r.o.OperatorMergePerf.oneStreamOfNthatMergesIn1 1000000 thrpt 5 35.925 35.730 33.948 35.526 35.611
```

The memory allocation amounts look good, though I don't understand the shape of the red graph at the top. That is making me question this.

![screen shot 2014-12-22 at 9 05 06 pm](https://cloud.githubusercontent.com/assets/813492/5534740/3c346190-8a1e-11e4-8a57-5ced2954eb8c.png)

With `finalize()` (as an experiment, aware of all the reasons to not use `finalize()` from Effective Java, 2nd Edition) this is what it looks like:

![screen shot 2014-12-22 at 9 05 56 pm](https://cloud.githubusercontent.com/assets/813492/5534748/657c3140-8a1e-11e4-8459-3dd92571c0a7.png)

The GC times on this one are higher than without `finalize()`:

![screen shot 2014-12-22 at 9 06 33 pm](https://cloud.githubusercontent.com/assets/813492/5534751/6e91e86a-8a1e-11e4-9cb6-2689edc5a143.png)

compared with:

![screen shot 2014-12-22 at 9 07 10 pm](https://cloud.githubusercontent.com/assets/813492/5534754/850f38e0-8a1e-11e4-9e8a-4fc230df8013.png)