RingBuffer contents and GC impact

358 views
Skip to first unread message

Tomas Salfischberger

unread,
Aug 5, 2012, 11:52:58 AM8/5/12
to lmax-di...@googlegroups.com
Hi,

I have been testing several different approaches to using disruptor in my application (which is currently SEDA based). One thing I'm still researching is what the best approaches is to picking the content-objects of the RingBuffer.

Basically I tried two approaches:

1. A reference based approach where the RingBuffer content is a wrapper which holds a reference to an Event (which is an interface with several immutable implementations for different messages). A new Event instance is created for each message coming in from the network, putting quite a high strain on the GC (also because of the aging wrappers which cause inter-generation pointers which are as far as I know not that good for GC performance).

2: Only 1 main Event class which holds a set of primitives (mostly bytes and longs directly mapped to the on-the-wire protocol), this contains an extra field indicating what type of event it is and has a set() method which resets all fields as if it was a constructor. This means there is no memory-allocation for new messages, just changing basic primitive fields. As you can imagine, the performance of this setup is sensational.

What I'm interested in is what you're using, did you make a trade-off between the clean programming-model of option 1 and the performance of option 2? Or did you take a completely different approach?

Tomas

Michael Barker

unread,
Aug 6, 2012, 1:25:37 AM8/6/12
to lmax-di...@googlegroups.com
I typically use solution #2 as most of time the contents of the ring
buffer are an event off of the wire in it's serialised form, i.e. a
byte array. Therefore most of the translation into an event in the
ring buffer is a simple array copy. Using #1 can work, but there are
a couple of niggles. Because you have a holder object for each event
you end up with a second indirection that the GC has to pass through,
which can extend the length of GC pauses, especially if your buffer is
large. Secondly, you need to manually null out the entry in the
buffer after your last event handler has completed otherwise your
objects in the array will end up hanging around for much longer and
therefore have a higher probability of escaping the new space again
increasing GC times.

Mike

Tomas Salfischberger

unread,
Aug 6, 2012, 3:26:17 AM8/6/12
to lmax-di...@googlegroups.com
Hi Mike,

Indeed, the holder-object GC pressure and moving up to older GC-generations making it even worse was exactly what I saw in my testing.

Regarding the byte-array, that requires deserializing on the business-logic side. I tried deserializing in the network thread, then write to a re-usable  Event-class, something like this:

public class Event {
    private short clientId;
    private long msgId;
    private byte msgType;

    /* some more fields + getters/setters here */

    public void set(short clientId, long msgId, byte msgType, /* etc... */) {
        this.clientId = clientId;
        /* etc */
    }
}

This saves de-serializing multiple times while still re-using objects (almost zero GC-pressure), but it makes for horrible to maintain code because every type of message has to fit in one class (complete OO anti-pattern).

I contemplated other approaches like a ByteBuffer as the ring content, then use re-usable objects within the consumers themselves with a readFrom(ByteBuffer) method. That would avoid GC-pressure, while still keeping the logic in the consumers as clean as possible. Is that what you meant by keeping byte[] as the contents? And what kind of de-serialization from the buffer do you use in that case? Roll-your-own reading raw bytes/longs?

Tomas

Michael Barker

unread,
Aug 6, 2012, 8:55:05 AM8/6/12
to lmax-di...@googlegroups.com
> Regarding the byte-array, that requires deserializing on the business-logic
> side. I tried deserializing in the network thread, then write to a re-usable
> Event-class, something like this:

We deserialise on the Business Logic side in order to do the least
amount of work possible on the network thread and keep object creation
and use on the same thread as much as possible.

> I contemplated other approaches like a ByteBuffer as the ring content, then
> use re-usable objects within the consumers themselves with a
> readFrom(ByteBuffer) method. That would avoid GC-pressure, while still
> keeping the logic in the consumers as clean as possible. Is that what you
> meant by keeping byte[] as the contents? And what kind of de-serialization
> from the buffer do you use in that case? Roll-your-own reading raw
> bytes/longs?

A roll your own solution, Java serialization is terrible.

Mike.
Reply all
Reply to author
Forward
0 new messages