LMAX Disruptor

repository·master·Indexed 12 days ago

https://github.com/lmax-exchange/disruptor

A high-performance inter-thread messaging library designed for low-latency, high-throughput concurrency. It utilizes a ring buffer pattern to minimize contention and maximize hardware efficiency, serving as an alternative to traditional queue-based messaging. Version 4.0.0 requires Java 11 and introduces updates to the BatchEventProcessor and EventHandler interfaces.

Tokens
7.2K
Snippets
15
Records
45
Agent score
96%

What's inside LMAX Disruptor

  1. Overview of LMAX Disruptor

    master
    LMAX Disruptor is a high-performance inter-thread messaging library. It is designed to facilitate extremely low-latency, high-throughput communication between threads, typically used in financial trading systems and other performance-critical applications where traditional queue-based concurrency models encounter contention bottlenecks.
  2. What is the LMAX Disruptor?

    master
    The LMAX Disruptor is a high-performance, inter-thread messaging library designed for low-latency and high-throughput concurrent programming. Unlike conventional approaches that use queues to pass data between stages (which can introduce latency due to locks and cache misses), the Disruptor uses a lock-free ring buffer architecture. It is designed with "mechanical sympathy" for modern hardware to minimize CPU-level cache misses and avoid costly kernel arbitration required by locks. It is a general-purpose concurrency framework, not limited to financial applications.
  3. What is the LMAX Disruptor and when should I use it?

    master

    The LMAX Disruptor is a high-performance alternative to bounded queues for exchanging data between concurrent threads. It is designed for asynchronous event processing architectures where high throughput and low latency are critical requirements.

    Unlike conventional queues that conflate the concerns of producers, consumers, and data storage (leading to contention and cache misses), the Disruptor separates these concerns using a pre-allocated ring-buffer. This design minimizes write contention, reduces concurrency overhead, and is highly cache-friendly, making it ideal for building low-latency pipelines, order matching engines, or real-time risk management systems.

  4. Recover from recoverable errors using Batch Rewind

    master

    When using a BatchEventProcessor to process events in batches, you can implement a recovery mechanism called "Batch Rewind". This allows the processor to automatically retry a batch from the beginning if a recoverable error occurs during processing.

    To trigger a rewind, your event handler must throw a RewindableException. When this exception is caught, the BatchEventProcessor consults the configured BatchRewindStrategy to decide whether to:

    1. Rewind the sequence number back to the start of the current batch to reattempt processing.
    2. Rethrow the exception and delegate to the standard ExceptionHandler.

    For example, if a batch contains sequences 150 through 155 and a RewindableException occurs at 153 using a SimpleBatchRewindStrategy, the execution flow will be: 150, 151, 152, 153(failed -> rewind), 150, 151, 152, 153(succeeded), 154, 155.

  5. Use extended EventHandler interfaces in 4.0.0

    master

    In version 4.0.0, several extension interfaces have been rolled up directly onto the EventHandler interface. You can now implement these directly on your event handler to access specific lifecycle or batching capabilities:

    • BatchStartAware: Allows handling the start of a batch.
    • LifecycleAware: Allows handling lifecycle events.
    • SequenceReportingEventHandler: Allows reporting sequences.
  6. Understand Disruptor latency performance characteristics

    master

    The Disruptor is designed to provide low and predictable latency compared to traditional bounded queues like ArrayBlockingQueue.

    Key performance characteristics include:

    • Low Mean Latency: In a three-stage pipeline, the Disruptor can achieve mean latency in the nanosecond range (e.g., ~52ns) compared to the microsecond range for ArrayBlockingQueue.
    • Predictable Latency (Flat 'J' Curve): Unlike many systems where latency increases exponentially as load increases (the 'J' curve), the Disruptor's latency remains almost flat until the memory sub-system reaches saturation.
    • Batching Effect: Consumers can process multiple entries up to a given threshold without contention, which helps maintain high throughput and low latency under load.
    • Contention Reduction: The architecture minimizes write contention and read contention by working efficiently with modern CPU caching mechanisms.
  7. Understand the performance implications of memory and cache

    master

    To achieve maximum performance with the Disruptor, it is important to understand how it interacts with modern CPU architectures:

    • Pre-allocation: All entries in the ring buffer are pre-allocated at startup. This makes them 'immortal' from the perspective of the Garbage Collector (GC), reducing GC pressure and avoiding 'stop the world' pauses. It also ensures data is laid out contiguously to support cache striding.
    • False Sharing: If two independent variables are written to by different threads but reside on the same cache line (typically 64 bytes), it causes performance degradation. The Disruptor's design aims to minimize this by separating producer and consumer concerns.
    • Memory Barriers: The Disruptor uses memory barriers (implemented via volatile in Java) to ensure that when a producer commits a sequence, the changes made to the ring buffer entries are immediately visible to the consumers.
    • Cache Striding: By using an array-backed ring buffer with a predictable access pattern, the Disruptor allows the CPU to effectively use its pre-fetcher to load data into cache before it is needed.
  8. Core Concepts of the Disruptor

    master

    The Disruptor is a low-latency, high-throughput concurrent ring buffer data structure. Understanding its domain language is essential for correct usage:

    • Ring Buffer: The storage mechanism for Events. It can be replaced by the user for advanced use cases.
    • Sequence: An identifier for a component's progress (e.g., a consumer's position). It functions similarly to an AtomicLong but includes padding to prevent false sharing.
    • Sequencer: The core engine that implements concurrent algorithms for passing data between producers and consumers.
    • Sequence Barrier: Logic that determines if events are available for a consumer by referencing the published Sequence and dependent consumer Sequences.
    • Wait Strategy: Determines how a consumer waits for new events (e.g., busy-spinning vs. blocking).
    • Event: The unit of data passed through the system, defined by the user.
    • Event Processor: The event loop for handling events; BatchEventProcessor is the standard efficient implementation.
    • Event Handler: A user-implemented interface representing a consumer.
    • Producer: User code that enqueues events into the Disruptor.
  9. Multicast Events and Consumer Dependency Graphs

    master

    Unlike a standard BlockingQueue where one event goes to one consumer, the Disruptor supports Multicast: every event is published to all registered consumers.

    To coordinate parallel consumers (e.g., ensuring business logic only runs after journaling and replication are complete), use a Consumer Dependency Graph via "gating":

    1. Prevent Producer Overrun: Use RingBuffer.addGatingConsumers() to add relevant consumers to the Disruptor.
    2. Coordinate Consumers: Construct a SequenceBarrier containing the Sequences of the components that must complete first.

    In a dependency chain, the Sequencer only needs to track the Sequence of the leaf nodes in the dependency tree to ensure it doesn't wrap the Ring Buffer.

  10. Event Pre-allocation

    master

    To minimize Garbage Collection (GC) pressure and latency, the Disruptor uses event pre-allocation.

    Instead of creating new objects for every message, you provide an EventFactory during construction. The Disruptor calls this factory to pre-populate the Ring Buffer. When publishing, you do not pass a new object; instead, you obtain a reference to the existing pre-allocated object in the buffer and update its fields. This ensures memory is reused rather than reallocated.

  11. How the Disruptor manages concurrency and sequencing

    master

    Concurrency in the Disruptor is managed through a strict sequencing concept rather than heavy-weight locks. This approach avoids the high cost of kernel context switches and arbitration associated with traditional locks.

    Key Mechanisms:

    • Ring Buffer: A pre-allocated, bounded data structure that stores entries. Using a power-of-2 size allows for efficient remainder calculation via bit masking.
    • Sequencing:
      • Producers claim a slot in the sequence. In single-producer scenarios, this is a simple counter. In multi-producer scenarios, it uses CAS (Compare And Swap) operations.
      • Consumers track their own sequences as they process entries. This allows producers to track consumer progress and prevent the ring buffer from wrapping around.
    • Barriers:
      • ProducerBarrier: Manages claiming slots and committing changes to make them visible to consumers.
      • ConsumerBarrier: Notifies consumers when new entries are available.
    • Batching Effect: When a consumer lags behind, it can process all available entries up to the current cursor in a single batch without re-engaging concurrency mechanisms, which helps the system regain pace quickly during bursts.
  12. Getting started with the LMAX Disruptor

    master

    To begin using the Disruptor, follow these recommended steps:

    1. Understand the core concepts: Read the Technical Paper to understand the underlying concurrency problems the Disruptor solves and its performance characteristics.
    2. Learn how to use it: Consult the User Guide for practical implementation instructions.
    3. Explore the API: Refer to the Javadoc for detailed technical specifications of the public interfaces.
    4. Troubleshoot and learn: Check the Frequently Asked Questions for common queries.