Codebase list golang-gopkg-eapache-queue.v1 / 9468035
Initial import of queue code from channels package Evan Huus 9 years ago
3 changed file(s) with 103 addition(s) and 2 deletion(s). Raw diff Collapse all Expand all
0 queue
0 Queue
11 =====
22
3 Fast golang queue using ring-buffer
3 A fast Golang queue using a ring-buffer, based on the version suggested by Dariusz Górecki.
4 Using this instead of other, simpler, queue implementations (slice+append or linked list) provides
5 substantial memory and time benefits, and fewer GC pauses.
6
7 The queue implemented here is as fast as it is in part because it is *not* thread-safe.
0 /*
1 Package queue provides a fast, ring-buffer queue based on the version suggested by Dariusz Górecki.
2 Using this instead of other, simpler, queue implementations (slice+append or linked list) provides
3 substantial memory and time benefits, and fewer GC pauses.
4
5 The queue implemented here is as fast as it is for two additional reasons: it is *not* thread-safe, and it
6 intentionally does not follow go best-practices regarding errors - if you make a mistake with this
7 queue (such as trying to remove an element from an empty queue) then who knows what will happen.
8 */
9 package queue
10
11 const minQueueLen = 16
12
13 type Queue struct {
14 buf []interface{}
15 head, tail, count int
16 }
17
18 // New constructs and returns a new Queue
19 func New() *Queue {
20 return &Queue{buf: make([]interface{}, minQueueLen)}
21 }
22
23 // Length returns the number of elements currently stored in the queue
24 func (q *Queue) Length() int {
25 return q.count
26 }
27
28 func (q *Queue) resize() {
29 newBuf := make([]interface{}, q.count*2)
30
31 if q.tail > q.head {
32 copy(newBuf, q.buf[q.head:q.tail])
33 } else {
34 copy(newBuf, q.buf[q.head:len(q.buf)])
35 copy(newBuf[len(q.buf)-q.head:], q.buf[:q.tail])
36 }
37
38 q.head = 0
39 q.tail = q.count
40 q.buf = newBuf
41 }
42
43 // Add puts an element on the end of the queue
44 func (q *Queue) Add(elem interface{}) {
45 if q.count == len(q.buf) {
46 q.resize()
47 }
48
49 q.buf[q.tail] = elem
50 q.tail = (q.tail + 1) % len(q.buf)
51 q.count++
52 }
53
54 // Peek returns the element at the head of the queue. If the queue is empty (Length == 0),
55 // Peek does not panic, it simply returns garbage.
56 func (q *Queue) Peek() interface{} {
57 return q.buf[q.head]
58 }
59
60 // Remove removes the element from the front of the queue. If you actually want the element,
61 // call Peek first. If the queue is empty (Length == 0), Remove will put the queue in a bad
62 // state and all further operations will be undefined.
63 func (q *Queue) Remove() {
64 q.buf[q.head] = nil
65 q.head = (q.head + 1) % len(q.buf)
66 q.count--
67 if len(q.buf) > minQueueLen && q.count*4 <= len(q.buf) {
68 q.resize()
69 }
70 }
0 package queue
1
2 import "testing"
3
4 // General warning: Go's benchmark utility (go test -bench .) increases the number of
5 // iterations until the benchmarks take a reasonable amount of time to run; memory usage
6 // is *NOT* considered. On my machine, these benchmarks hit around ~1GB before they've had
7 // enough, but if you have less than that available and start swapping, then all bets are off.
8
9 func BenchmarkQueueSerial(b *testing.B) {
10 q := New()
11 for i := 0; i < b.N; i++ {
12 q.Add(nil)
13 }
14 for i := 0; i < b.N; i++ {
15 q.Remove()
16 }
17 }
18
19 func BenchmarkQueueTickTock(b *testing.B) {
20 q := New()
21 for i := 0; i < b.N; i++ {
22 q.Add(nil)
23 q.Remove()
24 }
25 }