6
6
// top of a slice/array. All operations are constant time except
7
7
// for PushFront and PushBack which are amortized constant time.
8
8
//
9
- // We are about 15%-45% faster than container/list at the price
10
- // of potentially wasting some memory because we grow by doubling.
11
- // We seem to even beat Go's channels by a small margin.
9
+ // We are almost twice as fast as container/list at the price of
10
+ // potentially wasting some memory because we grow by doubling.
11
+ // We are also faster than Go's channels by a smaller margin.
12
12
package queue
13
13
14
14
import "fmt"
@@ -18,7 +18,8 @@ import "fmt"
18
18
type Queue struct {
19
19
// PushBack writes to rep[back] and then increments
20
20
// back; PushFront decrements front and then writes
21
- // to rep[front]; gotta love those invariants.
21
+ // to rep[front]; len(rep) must be a power of two;
22
+ // gotta love those invariants.
22
23
rep []interface {}
23
24
front int
24
25
back int
@@ -32,19 +33,13 @@ func New() *Queue {
32
33
33
34
// Init initializes or clears queue q.
34
35
func (q * Queue ) Init () * Queue {
35
- // start with a slice of length 2 even if that "wastes"
36
- // some memory; we do front/back arithmetic modulo the
37
- // length, so starting at 1 would require special cases
38
- q .rep = make ([]interface {}, 2 )
39
- // for some time I considered reusing the existing slice
40
- // if all a client does is re-initialize the queue; the
41
- // big problem with that is that the previous queue might
42
- // have been huge while the current queue doesn't grow
43
- // much at all; if that were to happen we'd hold on to a
44
- // huge chunk of memory for just a few elements and nobody
45
- // could do anything about it; so instead I decided to
46
- // just allocate a new slice and let the GC take care of
47
- // the previous one; seems a better tradeoff all around
36
+ q .rep = make ([]interface {}, 1 )
37
+ // I considered reusing the existing slice if all a client does
38
+ // is re-initialize the queue. The problem is that the current
39
+ // queue might be huge, but the next one might not grow much. So
40
+ // we'd hold on to a huge chunk of memory for just a few elements
41
+ // and nobody can do anything. Making a new slice and letting the
42
+ // GC take care of the old one seems like a better tradeoff.
48
43
q .front , q .back , q .length = 0 , 0 , 0
49
44
return q
50
45
}
@@ -81,8 +76,9 @@ func (q *Queue) full() bool {
81
76
func (q * Queue ) grow () {
82
77
bigger := make ([]interface {}, q .length * 2 )
83
78
// Kudos to Rodrigo Moraes, see https://gist.github.com/moraes/2141121
84
- copy (bigger , q .rep [q .front :])
85
- copy (bigger [q .length - q .front :], q .rep [:q .front ])
79
+ // Kudos to Dariusz Górecki, see https://github.com/eapache/queue/commit/334cc1b02398be651373851653017e6cbf588f9e
80
+ n := copy (bigger , q .rep [q .front :])
81
+ copy (bigger [n :], q .rep [:q .front ])
86
82
// The above replaced the "obvious" for loop and is a bit tricky.
87
83
// First note that q.front == q.back if we're full; if that wasn't
88
84
// true, things would be more complicated. Second recall that for
@@ -122,13 +118,13 @@ func (q *Queue) String() string {
122
118
// inc returns the next integer position wrapping around queue q.
123
119
func (q * Queue ) inc (i int ) int {
124
120
l := len (q .rep )
125
- return (i + 1 + l ) % l
121
+ return (i + 1 ) & ( l - 1 ) // requires l = 2^n
126
122
}
127
123
128
124
// dec returns the previous integer position wrapping around queue q.
129
125
func (q * Queue ) dec (i int ) int {
130
126
l := len (q .rep )
131
- return (i - 1 + l ) % l
127
+ return (i - 1 ) & ( l - 1 ) // requires l = 2^n
132
128
}
133
129
134
130
// Front returns the first element of queue q or nil.
0 commit comments