This project is a Java-based benchmarking tool for evaluating the performance of different queue implementations.
The Queue interface defines the basic operations for a queue, including:
boolean try_push(T item): Attempts to add an element to the queue, if successful, returns true.T try_pop(): Attempts to remove an element from the queue, if successful, returns the element.
The ConcurrentQueue interface extends the Queue interface, it does not
provide additional methods but indicates that the queue is designed for
concurrent access without blocking.
The BlockingQueue interface extends the ConcurrentQueue interface and
provides additional methods for blocking operations:
void wait_push(T item) throws InterruptedException: Blocks until an element can be added to the queue.T wait_pop() throws InterruptedException: Blocks until an element can be removed from the queue.
The QueueFactory interface provides a method for creating instances of
queues:
Queue<T> create(): Creates a new instance of a queue.
Specialized factory for ConcurrentQueue implementations.
@Override ConcurrentQueue<T> create()
Specialized factory for BlockingQueue implementations.
@Override BlockingQueue<T> create()
The QueueFactoryDecorator interface provides a method for creating decorated
queue factories with additional functionality:
<T> QueueFactory<T> decorate(QueueFactory<T> factory): Decorates a queue factory with additional functionality and returns a new instance of the decorated factory or null if the factory cannot be decorated.
The Bench interface provides methods for running benchmarks on queue
implementations:
double[] run(QueueFactory<Integer> queue, long nop): Runs the benchmark on the specified queue implementation and returns an array of results or null if the benchmark does not support the specified queue.nopis the time between two consecutive calls toSystem.nanoTime()in nanoseconds.
A wrapper around java.util.concurrent.ArrayBlockingQueue that implements the
BlockingQueue interface.
A wrapper around java.util.ArrayDeque that implements the Queue interface.
A wrapper around java.util.concurrent.ConcurrentLinkedQueue that implements
the ConcurrentQueue interface.
A wrapper around java.util.concurrent.LinkedBlockingQueue that implements the
BlockingQueue interface.
A wrapper around java.util.LinkedList that implements the Queue interface.
A custom, concurrent lock-free array-based queue implementation that implements
the ConcurrentQueue interface.
A custom linked list-based queue implementation that implements the Queue
interface.
A spinlock implementation, using a single AtomicBoolean, that can be used to
enhance Queues into ConcurrentQueues.
A ticket lock implementation that can be used to enhance Queues into
ConcurrentQueues.
A synchronized decorator that provides thread-safe and blocking behaviour to queue operations using Java's built-in synchronization mechanisms.
A lock decorator that provides thread-safe and blocking behaviour to queue
operations using Java's ReentrantLock.
A lock decorator that provides thread-safe and blocking behaviour to queue
operations using Java's ReentrantLock with two Condition objects, one to
wait when the queue is full and the other to wait when the queue is empty.
A simple benchmark measuring the performance of queue operations in a single-threaded environment.
A benchmark with r+w threads, where r are readers and w are writers. It
measures the performance of concurrent queue operations with backlogged readers
and writers.
Analogous benchmarks is available for BackloggedBlockingBench(r,w).
A benchmark with w+1 threads.
w threads are repeatedly writing to a blocking queue, while one thread is
reading one element every 10us.
It measures the performance of each read operation.
Analogous benchmarks are available for BurstBlockingWriteBench(w),
BurstConcurrentReadBench(r) and BurstConcurrentWriteBench(w).
There is no one-size-fits-all solution, the best choice depends on the specific use case and workload. However, some general observations can be made:
In a single threaded environment:
JavaArrayDequeandLinkedListperform the best, butJavaArrayDequeis more consistent.- Java's standard implementation of the linked list,
JavaLinkedList, performs worse than our customLinkedList. - Introducing locks or synchronization adds significant overhead.
In a blocking environment:
Lockperforms consistently better thanSynchronized.Lock2does not show significant advantages overLock, onBurstBlockingWriteBenchit actually performs considerably worse.- For decorated queues, the decorator choice has a much larger impact on performance than the underlying implementation.
- Surprisingly,
JavaArrayBlockingQueueperforms poorly compared toLock(JavaArrayDeque)except inBackloggedBlockingBench(1,1). JavaLinkedBlockingQueueperforms better during reads and worse during writes compared toLock(LinkedList).
When read operations are more important the best choice is JavaLinkedBlockingQueue, when write operations are more important the best choice is Lock(LinkedList).
In a concurrent non-blocking environment:
TicketLockoutperformsSpinLockin most benchmarks, also it reduces variability thanks to its fairness property.- On average
TickerLockperforms worse than locking decorators such asSynchronized,LockandLock2but the 3rd quartile is better. - As for blocking queues, the decorator choice has a much larger impact on performance than the underlying implementation.
- With low contention
ConcurrentArrayQueueperforms better thanJavaArrayBlockingQueuebut is worse with high contention, showing poor scalability. - With low contention
JavaConcurrentLinkedQueueperforms better thanJavaLinkedBlockingQueuebut is worse with high contention, showing poor scalability. JavaConcurrentLinkedQueuegenerally outperformsConcurrentArrayQueue, except inBackloggedConcurrentBenchwith more writers than readers.JavaConcurrentLinkedQueueis overall slightly better thanLock(LinkedList), especially comparing the 3rd quartile.
With a low number of threads the best choice is JavaConcurrentLinkedQueue, with a high number of threads the best choice is Lock(LinkedList).