Label: Go/Lock-free Queue/Benchmark/Performance Test/zhenyi-base

zhenyi-base zqueue benchmark test sharing

For complete data see docs/benchmark/zqueue_matrix_results.txt

Introduction

In the previous series of articles, we introduced various concurrent queues implemented in zhenyi-base one by one - from the basic lock-free SPSC/MPSC ring queue to the sync.Mutex of Queue[T](Two strategies: discard when full/expand when full), double-buffered SmartDouble, heap-structured priority queue, and Go native channel as a comparison baseline. We have specifically discussed the design choices and applicable scenarios of each queue.

In this issue we won’t talk about the principles, but only one thing:How fast are these queues?

This article is based on a unified benchmark testing framework and conducts a matrix stress test covering "load size × concurrency × API mode" for all queues in a fixed environment. The data speaks for itself - not only latency, but also memory allocation and throughput are looked at, to help you have a clear understanding of the actual selection.

However, benchmark testing is greatly affected by the environment, parameters, and implementation details. It is inevitable that the measurement will be inaccurate or the comparison will not be fair enough. If there are deviations in the data in the article, or if you feel that the comparison is not rigorous enough, please point it out in the comment area or issue - let's work together to make the test more accurate and objective.

1. Test environment

projectConfiguration
platformLinux
CPUIntel(R) Xeon(R) CPU @ 2.20GHz
Go version1.24
Test concurrencyP1 / P4 / P16 / P64
Load sizeSmall(256B) / Medium(4KB) / Large(64KB)
API patternSingleSingle / SingleBatch / BatchSingle / BatchBatch

Notice: SmartDouble only supports SingleBatch / BatchBatch APIs; Priority only supports SingleSingle. SPSC queue (SPSCBounded / SPSCUnbounded) only has P1 concurrency data.


2. Performance data of each queue

2.1 SPSC Queue (SPSCBounded / SPSCUnbounded)

The SPSC queue only has a single producer and single consumer scenario, and only has P1 concurrency.

2.1.1 SPSCBounded (bounded SPSC ring queue)
loadP1/SingleSingle (ns/op)P1/BatchBatch (ns/op)MB/s (BatchBatch)
Small77.4450.335086
Medium986.4847.14835
Large21530198403303

Memory allocation: Small/Medium 0 B/op, Large/BatchBatch 17 B/op.

zhenyi-benchmark-chart7.png

2.1.2 SPSCUnbounded (unbounded SPSC linked list queue)
loadP1/SingleSingle (ns/op)P1/BatchBatch (ns/op)MB/s (BatchBatch)
Small191.4168.61518
Medium829.8768.85327
Large20843245262671

Memory allocation: Small 0 B/op; Medium 1to6 B/op; Large 405to764 B/op.

zhenyi-benchmark-chart10.png


2.2 MPSCBounded / MPSCPadded (bounded MPSC)

loadMPSCBounded P1/Single (ns/op)MPSCBounded P64/BatchBatch (ns/op)MPSCPadded P1/Single (ns/op)MPSCPadded P64/BatchBatch (ns/op)
Small111.555.76111.655.61
Medium994.9933.8875.5932.8
Large20132185161964918512

Small/Medium P1/Single and Small P64/BatchBatch are 0 B/op; Medium/Large P64/BatchBatch has a small amount of allocation (25 to 994 B/op), and the overall level is still very low.

zhenyi-benchmark-chart8.png


2.3 MPSCUnbounded (unbounded MPSC linked list queue)

loadP1/Single (ns/op)P64/BatchBatch (ns/op)Distribution (P64/BatchSingle)
Small273.5356.0203 B/op
Medium919.418834514 B/op
Large218602796659489 B/op

MPSCUnbounded Each load × Concurrency delay(ns/op)

MPSCUnbounded memory allocation(B/op)


2.4 Queue[T] (mutex-based ring queue)

QueueResize and QueueDrop share the same set Queue[T] accomplish(sync.Mutex ring queue), the only difference is thatProcessing strategy when the queue is full.

2.4.1 QueueDrop — FullPolicyDrop (discard when full)

When the queue is full Enqueue() Return directly false, no blocking, no expansion, no memory allocation.

loadP1/Single (ns/op)P4/Single (ns/op)P16/Single (ns/op)P64/Single (ns/op)
Small180.4155.8176.5201.3
Medium19121760565.7525.7
Large29829133171047512216

Memory allocation: all scenarios 0 B/op, 0 allocs/op.

QueueDrop Each load × Concurrency delay(ns/op)

Throughput with full queue drops:exist BatchSingle / BatchBatch In mode, if the capacity is exceeded, a failure will be returned directly and zero memory allocation will occur. Therefore, high-concurrency batch enqueueing can maintain extremely high throughput (reference: SPSCBounded Small P1/BatchBatch is 5086 MB/s).

loadP1 (MB/s)P4 (MB/s)P16 (MB/s)P64 (MB/s)
Small2987203861902319722
Medium3529322653133032694
Large3220161701814918570

QueueDrop BatchSingle Throughput comparison(MB/s)

QueueDrop SingleSingle Throughput(MB/s)

2.4.2 QueueResize — FullPolicyResize (expansion when full)

Automatically expand the queue when it is full (cap × 2), joining the queue will never fail, but memory will be allocated during expansion.

loadP1/Single (ns/op)P4/Single (ns/op)P16/Single (ns/op)P64/Single (ns/op)
Small225.5518.1832.3757.8
Medium1967240467604728
Large6494777979128132103230

Memory allocation: Small P1 3 B/op,P4 to P64 505to973 B/op; Medium P1 0 B/op,P4 to P64 933to11353 B/op; Large 103274to201305 B/op.

QueueResize Each load × Concurrency delay(ns/op)


2.5 SmartDouble (double buffer queue)

SmartDouble only supports SingleBatch / BatchBatch API, the following table uses BatchBatch.

loadP1/BatchBatch (ns/op)P4/BatchBatch (ns/op)P16/BatchBatch (ns/op)P64/BatchBatch (ns/op)
Small49.0439.6951.87113.7
Medium723.9867.5889.21028
Large17318168281548816621

Memory allocation: Small 0 B/op;Medium P4/P16/P64 respectively 1/6/27 B/op;Large P1/P4/P16/P64 respectively 14/57/217/975 B/op.

Implementation method: double buffering + sync.Mutex.

SmartDouble Each load × Concurrency delay(ns/op)

SmartDouble memory allocation(B/op)


2.6 Priority (priority queue)

Priority only supports SingleSingle API.

loadP1/Single (ns/op)P4/Single (ns/op)P16/Single (ns/op)P64/Single (ns/op)
Small599.6172324651877
Medium3429209142215321347
Large64271361545344674296466

Memory allocation: Small P1/P4/P16/P64 respectively 2/542/1040/1163 B/op;Medium P4/P16/P64 respectively 9840/19179/20010 B/op;Large P4/P16/P64 respectively 186024/250971/250974 B/op.

Implementation method: heap structure, enqueuing/dequeuing O(log n).

zhenyi-benchmark-chart14.png

zhenyi-benchmark-chart19.png


2.7 ChanBuffered / ChanUnbuffered (Go standard channel baseline)

loadChanBuffered P1/Single (ns/op)ChanUnbuffered P1/Single (ns/op)
Small168.9353.1
Medium1575481.0
Large188786103

Memory allocation: all scenarios 0 B/op, 0 allocs/op.

zhenyi-benchmark-chart22.png


3. Overall comparison across cohorts

The following comparisons all use the SingleSingle API. SmartDouble does not support this API and has excluded it. The SPSC cohort has only P1 data and was excluded from the P64 comparison.

3.1 Latency comparison of each queue (different loads × concurrency)

zhenyi-benchmark-chart4.png zhenyi-benchmark-chart15.png zhenyi-benchmark-chart16.png zhenyi-benchmark-chart17.png

3.2 Summary of memory allocation for each queue

The memory allocation amount of SingleSingle API for each queue at its maximum concurrency (P64 or P1) (SPSC queue only has P1 data). SPSCBounded Large peaks at about 17 B/op in BatchBatch.

queueSmall (B/op)Large (B/op)
SPSCBounded (P1)00
MPSCBounded (P64)00
MPSCPadded (P64)00
QueueDrop (P64)00
MPSCUnbounded (P64)18252955
QueueResize (P64)973201305
Priority (P64)1163250974
ChanBuffered (P64)00
ChanUnbuffered (P64)00

zhenyi-benchmark-chart6.png


4. API and concurrency analysis

4.1 Batch API vs Single API

Comparison of ns/op between batch API and single API under the same queue:

queueloadSingle shot (ns/op)batch (ns/op)
MPSCBoundedSmall/P1111.5 (SingleSingle)74.12 (BatchBatch)
MPSCBoundedSmall/P64147.7 (SingleSingle)55.76 (BatchBatch)
SPSCBoundedSmall/P177.44 (SingleSingle)50.33 (BatchBatch)
QueueDropSmall/P4155.8 (SingleSingle)12.56 (BatchSingle)

zhenyi-benchmark-chart2.png


4.2 Impact of concurrency on performance

by MPSCBounded/Small for example:

ConcurrencySingleSingle (ns/op)BatchBatch (ns/op)
P1111.574.12
P4141.352.61
P16144.354.08
P64147.755.76

zhenyi-benchmark-chart3.png


5. Complete data and reproduction

Complete benchmark (114 sets of tests):

📎 docs/benchmark/zqueue_matrix_results.txt

Reproduction method:

git clone https://github.com/aiyang-zh/zhenyi-base.git
cd zhenyi-base/zqueue
go test -bench=BenchmarkMatrix -benchmem -count=3 > result.txt

⭐ Click a star if you find it helpful. If you have any questions, please ask Issue

Complete source code:github.com/aiyang-zh/z…


Communication group: QQ group 1098078562

Official account: Zhenyi-io