Netflix/RxJava — pull request #1281 by benjchristensen (2014-05-29 19:14:50+00:00)
Title: Reduce Subscription Object Allocation

### Changes

- rewrite of `CompositeSubscription` and added `ChainedSubscription`
- significant reduction in object allocations
- details on research available at https://github.com/Netflix/RxJava/issues/1204

##### Atomic State Machine -> Mutation & Locks

The `CompositeSubscription` implementation was changed from using an atomic state machine to using locks. The state machine requires immutable `State` that is swapped using CAS. This means an object allocation is needed each time.

It now uses locks to protect mutable state so very few objects are created.

##### ChainedSubscription

The `CompositeSubscription` requires support of randomly removing a `Subscription` via the `remove` method. The `Subscriber` type does not expose this so can be optimized. There is now a `ChainedSubscription` that is used by `Subscriber` instead. This allows using a `LinkedList` or `ArrayList` rather than `HashSet` as random access is never needed. This provides a slight performance boost and reduces memory usage (1 minute test shows 16.5GB allocation for `Composite` versus 14.4GC for `Chained`).

### Allocation Comparison

This shows Java Flight Recorder output from master without these changes:

![master-without-changing-compositesubscription](https://cloud.githubusercontent.com/assets/813492/3122035/66ea4e22-e762-11e3-8915-5aead429e120.png)

This shows object allocation after changing `CompositeSubscription`:

![master-with-new-compositesubscription ong](https://cloud.githubusercontent.com/assets/813492/3122195/9f3641bc-e764-11e3-930f-845efc99b84a.png)

### Throughput Comparison

Testing this code:

```java
public void mapTransformation(UseCaseInput input) throws InterruptedException {
input.observable.map(i -> {
return String.valueOf(i);

}).map(i -> {
return Integer.parseInt(i);
}).subscribe(input.observer);
input.awaitCompletion();
}
```

##### Rx 0.16.1

```
Run: 10 - 2,879,355 ops/sec
Run: 11 - 3,236,245 ops/sec
Run: 12 - 4,468,275 ops/sec
Run: 13 - 3,237,293 ops/sec
Run: 14 - 4,683,840 ops/sec
```

##### Rx 0.17.6 - using OnSubscribeFunc

```
Run: 10 - 3,621,876 ops/sec
Run: 11 - 6,702,412 ops/sec
Run: 12 - 7,401,924 ops/sec
Run: 13 - 6,653,359 ops/sec
Run: 14 - 5,834,305 ops/sec
```

##### Rx 0.17.6 - using OnSubscribe

```
Run: 10 - 3,320,053 ops/sec
Run: 11 - 4,520,795 ops/sec
Run: 12 - 7,107,320 ops/sec
Run: 13 - 5,089,058 ops/sec
Run: 14 - 5,534,034 ops/sec

Run: 10 - 4,930,966 ops/sec
Run: 11 - 6,119,951 ops/sec
Run: 12 - 7,062,146 ops/sec
Run: 13 - 6,514,657 ops/sec
Run: 14 - 6,369,426 ops/sec
```

##### Rx 0.18.3 - using OnSubscribe

```
Run: 10 - 4,178,854 ops/sec
Run: 11 - 4,446,420 ops/sec
Run: 12 - 5,458,515 ops/sec
Run: 13 - 8,006,405 ops/sec
Run: 14 - 7,849,293 ops/sec

Run: 10 - 4,145,936 ops/sec
Run: 11 - 6,553,079 ops/sec
Run: 12 - 7,645,259 ops/sec
Run: 13 - 7,385,524 ops/sec
Run: 14 - 7,830,853 ops/sec
```

##### Rx 0.19 master - CompositeSubscription state machine

```
Run: 10 - 10,576,302 ops/sec
Run: 11 - 10,518,786 ops/sec
Run: 12 - 10,554,646 ops/sec
Run: 13 - 10,314,063 ops/sec
Run: 14 - 10,666,439 ops/sec
```

##### Rx 0.19 master - CompositeSubscription with synchronized HashSet

```
Run: 10 - 9,949,060 ops/sec
Run: 11 - 10,122,379 ops/sec
Run: 12 - 10,018,032 ops/sec
Run: 13 - 10,072,522 ops/sec
Run: 14 - 10,132,636 ops/sec
```

##### Rx 0.19 master - ChainedSubscription with synchronized ArrayList

```
Run: 10 - 11,086,351 ops/sec
Run: 11 - 10,932,426 ops/sec
Run: 12 - 11,002,431 ops/sec
Run: 13 - 10,888,620 ops/sec
Run: 14 - 11,157,227 ops/sec

Run: 10 - 9,371,192 ops/sec
Run: 11 - 9,829,169 ops/sec
Run: 12 - 10,139,005 ops/sec
Run: 13 - 10,099,785 ops/sec
Run: 14 - 10,017,631 ops/sec
```

##### Rx 0.19 master - ChainedSubscription with synchronized LinkedList

```
Run: 10 - 10,619,431 ops/sec
Run: 11 - 11,063,293 ops/sec
Run: 12 - 11,001,463 ops/sec
Run: 13 - 11,054,243 ops/sec
Run: 14 - 10,898,826 ops/sec

Run: 10 - 10,075,465 ops/sec
Run: 11 - 9,780,716 ops/sec
Run: 12 - 9,885,134 ops/sec
Run: 13 - 9,584,143 ops/sec
Run: 14 - 10,000,700 ops/sec
```