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
| project | Configuration |
|---|---|
| platform | Linux |
| CPU | Intel(R) Xeon(R) CPU @ 2.20GHz |
| Go version | 1.24 |
| Test concurrency | P1 / P4 / P16 / P64 |
| Load size | Small(256B) / Medium(4KB) / Large(64KB) |
| API pattern | SingleSingle / 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)
| load | P1/SingleSingle (ns/op) | P1/BatchBatch (ns/op) | MB/s (BatchBatch) |
|---|---|---|---|
| Small | 77.44 | 50.33 | 5086 |
| Medium | 986.4 | 847.1 | 4835 |
| Large | 21530 | 19840 | 3303 |
Memory allocation: Small/Medium 0 B/op, Large/BatchBatch 17 B/op.

2.1.2 SPSCUnbounded (unbounded SPSC linked list queue)
| load | P1/SingleSingle (ns/op) | P1/BatchBatch (ns/op) | MB/s (BatchBatch) |
|---|---|---|---|
| Small | 191.4 | 168.6 | 1518 |
| Medium | 829.8 | 768.8 | 5327 |
| Large | 20843 | 24526 | 2671 |
Memory allocation: Small 0 B/op; Medium 1to6 B/op; Large 405to764 B/op.

2.2 MPSCBounded / MPSCPadded (bounded MPSC)
| load | MPSCBounded P1/Single (ns/op) | MPSCBounded P64/BatchBatch (ns/op) | MPSCPadded P1/Single (ns/op) | MPSCPadded P64/BatchBatch (ns/op) |
|---|---|---|---|---|
| Small | 111.5 | 55.76 | 111.6 | 55.61 |
| Medium | 994.9 | 933.8 | 875.5 | 932.8 |
| Large | 20132 | 18516 | 19649 | 18512 |
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.

2.3 MPSCUnbounded (unbounded MPSC linked list queue)
| load | P1/Single (ns/op) | P64/BatchBatch (ns/op) | Distribution (P64/BatchSingle) |
|---|---|---|---|
| Small | 273.5 | 356.0 | 203 B/op |
| Medium | 919.4 | 1883 | 4514 B/op |
| Large | 21860 | 27966 | 59489 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.
| load | P1/Single (ns/op) | P4/Single (ns/op) | P16/Single (ns/op) | P64/Single (ns/op) |
|---|---|---|---|---|
| Small | 180.4 | 155.8 | 176.5 | 201.3 |
| Medium | 1912 | 1760 | 565.7 | 525.7 |
| Large | 29829 | 13317 | 10475 | 12216 |
Memory allocation: all scenarios 0 B/op, 0 allocs/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).
| load | P1 (MB/s) | P4 (MB/s) | P16 (MB/s) | P64 (MB/s) |
|---|---|---|---|---|
| Small | 2987 | 20386 | 19023 | 19722 |
| Medium | 3529 | 32265 | 31330 | 32694 |
| Large | 3220 | 16170 | 18149 | 18570 |


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.
| load | P1/Single (ns/op) | P4/Single (ns/op) | P16/Single (ns/op) | P64/Single (ns/op) |
|---|---|---|---|---|
| Small | 225.5 | 518.1 | 832.3 | 757.8 |
| Medium | 1967 | 2404 | 6760 | 4728 |
| Large | 64947 | 77979 | 128132 | 103230 |
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.

2.5 SmartDouble (double buffer queue)
SmartDouble only supports SingleBatch / BatchBatch API, the following table uses BatchBatch.
| load | P1/BatchBatch (ns/op) | P4/BatchBatch (ns/op) | P16/BatchBatch (ns/op) | P64/BatchBatch (ns/op) |
|---|---|---|---|---|
| Small | 49.04 | 39.69 | 51.87 | 113.7 |
| Medium | 723.9 | 867.5 | 889.2 | 1028 |
| Large | 17318 | 16828 | 15488 | 16621 |
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.


2.6 Priority (priority queue)
Priority only supports SingleSingle API.
| load | P1/Single (ns/op) | P4/Single (ns/op) | P16/Single (ns/op) | P64/Single (ns/op) |
|---|---|---|---|---|
| Small | 599.6 | 1723 | 2465 | 1877 |
| Medium | 3429 | 20914 | 22153 | 21347 |
| Large | 64271 | 361545 | 344674 | 296466 |
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).


2.7 ChanBuffered / ChanUnbuffered (Go standard channel baseline)
| load | ChanBuffered P1/Single (ns/op) | ChanUnbuffered P1/Single (ns/op) |
|---|---|---|
| Small | 168.9 | 353.1 |
| Medium | 1575 | 481.0 |
| Large | 18878 | 6103 |
Memory allocation: all scenarios 0 B/op, 0 allocs/op.

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)

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.
| queue | Small (B/op) | Large (B/op) |
|---|---|---|
| SPSCBounded (P1) | 0 | 0 |
| MPSCBounded (P64) | 0 | 0 |
| MPSCPadded (P64) | 0 | 0 |
| QueueDrop (P64) | 0 | 0 |
| MPSCUnbounded (P64) | 182 | 52955 |
| QueueResize (P64) | 973 | 201305 |
| Priority (P64) | 1163 | 250974 |
| ChanBuffered (P64) | 0 | 0 |
| ChanUnbuffered (P64) | 0 | 0 |

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:
| queue | load | Single shot (ns/op) | batch (ns/op) |
|---|---|---|---|
| MPSCBounded | Small/P1 | 111.5 (SingleSingle) | 74.12 (BatchBatch) |
| MPSCBounded | Small/P64 | 147.7 (SingleSingle) | 55.76 (BatchBatch) |
| SPSCBounded | Small/P1 | 77.44 (SingleSingle) | 50.33 (BatchBatch) |
| QueueDrop | Small/P4 | 155.8 (SingleSingle) | 12.56 (BatchSingle) |

4.2 Impact of concurrency on performance
by MPSCBounded/Small for example:
| Concurrency | SingleSingle (ns/op) | BatchBatch (ns/op) |
|---|---|---|
| P1 | 111.5 | 74.12 |
| P4 | 141.3 | 52.61 |
| P16 | 144.3 | 54.08 |
| P64 | 147.7 | 55.76 |

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
