Skip to content

Which Queue Should I Use?

Nitsan Wakart edited this page Jan 21, 2018 · 2 revisions

The right queue for your use case depends on a few factors:

  • Producer/Consumer thread counts: If you know upfront you will only have a single Consumer thread or a single Producer thread JCTools offers you specialized queues to capitalize on this property. The queue all have a prefix which tells you which usecase they fit: SPSC(Single Producer Single Consumer), MPSC(Multi Producer Single Consumer), SPMC(Single Producer Multi Consumer), MPMC(Multi Producer Multi Consumer). You can expect the single threaded side of the queue to offer reduced costs(better performance).
  • Bounded/Unbounded: Do you want an unbounded queue? this is generally not such a great idea, but is sometimes required. The Spsc/Mpsc/Spmc/MpscArrayQueue implementations are bounded as are the Chunked/GrowableArrayQueue variants for Spsc/Mpsc. The Spsc/MpscLinkedQueue implementations are unbounded as are the Spsc/MpscUnboundedArrayQueue.
  • Memory pressure: what footprint should an empty queue have? For some cases where many queues need to be maintained per application (e.g. actor systems) we want to minimize the footprint per queue. The Spsc/MpscLinkedQueue offer a minimal footprint for unbounded queues. The Chunked/GrowableArrayQueue variants for Spsc/Mpsc offer a lower footprint bounded variant(an empty queue maintains a single chunk, which can be a small array). Finally the Spsc/MpscUnboundedArrayQueue offer a compromise between upfront allocation and overhead per element and throughput.

In particular, the linked array queues offer a compromise between allocation+throughput and memory footprint. A linked array queue comes in 3 flavours:

  1. Growable: The backing array size will double each time the occupancy exceeds the array size until finally the array is at the maximum size. This allows idle queues to remain small while busy queues will grow to accommodate bursts. The maximum footprint, once settled, is as if you allocated the buffer upfront. There's a transitional period however where the final array is allocated and all the in between buffers are full. At this point the total footprint would be the sum of all the arrays. While this using this queue may result in allocation on the fly, the maximum amount of garbage generated is known upfront. The queue does not shrink once it has grown. This is a bounded queue.
  2. Chunked: The queue will used a linked list of fixed size arrays (chunks). As the consumer catches up with the producer the redundant chunks will be left for GC to pick up and the footprint will shrink. Overflowing a chunk will lead to garbage and the queue can lead to continuous allocation throughout the application lifetime. How often the chunk overflows will depend on sizing and burst size. This is a bounded queue.
  3. Unbounded: This is essentially the same as Chunked, but no limit is set on growth.

The linked array queues offer improved throughput and less allocation than a traditional linked queue. Their minimal memory footprint is larger than the traditional linked queue however.