Release of Disruptor 3.0.0
I’ve decided that I’m a bit bored of the whole putting a beta tag on various versions of the Disruptor so I’ve decide to send forth Disruptor 3.0.0 into the world. The big challenges of this release were to clean up the code and come up with a better algorithm for handling multiple producers. If I was lucky, make it even faster. I went down a couple of dark alleys initially with this release, but have come up again for air with a version that is less different to the 2.x version, but still brings some nice benefits. I had wanted to implement a few more functional tests and improve the documentation, but I could be waiting forever to those aspects to a level where I was 100% satisfied.
Improved Support for Multiple Producers
One of the really nasty problems with the 2.x version of the Disruptor is that it came with 2 implementations of the ClaimStrategy. One was for the situations where there was more threads than CPUs and one for when there was less. This was a little bit ugly. Version 3 now has a single MultiProducerSequencer (more on Sequencers/ClaimStrategies later) that should work well in both cases. It is a but slower for the un-contended case that our previous implementation around 18M ops/sec v 15M ops/sec*, but should scale much better as the number of threads increased and exceeds the CPU count. I did a couple of talks about the new implementation at TechMesh and JAX London last year. If you are interested in the specifics of the algorithm a YouTube video of the JAX London talk is available
No More Claim Strategies
One of the major changes to the internal design and API is that I have gotten rid of the ClaimStrategy interface. With some of the changes that I wanted to make to the multiple producer use case it got cumbersome. So I deleted it and instead provide 2 implementations of the Sequencer, SingleProducerSequencer and MultiProducerSequencer that covers the 2 important use cases. We no longer allow users to plug-in there own implementation, sorry about that, but the code became much cleaner, simpler and faster as a result.
Faster for the Single Producer Case
The one producer to one consumer benchmark* shows around 220M ops/sec for version 3 compared to 80M ops/sec for version 2. This has been mostly the result of general clean up (e.g. removing ClaimStrategies) and a few small optimisations.
Improved EventTranslator API for the RingBuffer
The EventTranslator API that the RingBuffer now supports now has variants for 1, 2, 3 and Varargs parameters to be passed through to the EventTranslator implementation. This means that input into the EventTranslator doesn’t have to be done via an anonymous inner class or pushing values through fields in the EventTranslator implementation. It also makes it easier to write EventTranslators as independent units that can be easily tested. Code isolation FTW!
Batch Producer Interface for the Sequencer
I wasn’t a big fan of our existing batch producer interface and initially I’d intended to dump it. After some thought I’ve added it partially back in. I.e. it is only available on the Sequencer. I may add it to the RingBuffer at a later point if I can come up with a clean safe API to expose the functionality. Out of interest I added a raw sequencer throughput test that uses the batch interface. This test does no real work, it just signals between the threads. However I have proved that the Disruptor can do nothing useful, in batches of 10 at more that 4 000 000 000 ops/sec. Not a useful benchmark in any case, but great for the bragging rights!
There were a few people that helped out getting the Disruptor from is version 2 state to version 3:
- Jason Koch: Manned up and did a huge amount of JavaDoc. I’ve been trying to keep it up to date since I merged his pull request.
- Dalibor Novak and Danny Yates: Github and Gradle migration.
- Adrian Sutton: Further work on the DSL.
- Martin Thompson: For his many useful ideas, especially around the algorithm to support multiple producers.
*All tests done on my Intel Core i7-3770 (Ivy Bridge) @ 3.40 GHz – YMMV.