RingBuffer: 100% cpu usage
sheldonlyr opened this issue · 7 comments
$uname -a
Linux Lee-Ubuntu 4.4.0-28-generic #47-Ubuntu SMP Fri Jun 24 10:09:13 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
$cat /etc/issue
Linux Lee-Ubuntu 4.4.0-28-generic #47-Ubuntu SMP Fri Jun 24 10:09:13 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
$go env
Linux Lee-Ubuntu 4.4.0-28-generic #47-Ubuntu SMP Fri Jun 24 10:09:13 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
Test code:
import (
"fmt"
"sync"
"time"
"github.com/Workiva/go-datastructures/queue"
)
var wg sync.WaitGroup
func main() {
wg.Add(2)
q := queue.NewRingBuffer(4096)
go func() {
defer wg.Done()
for {
time.Sleep(3 * time.Second)
q.Offer("hello world")
}
}()
go func() {
defer wg.Done()
for {
str, _ := q.Get()
fmt.Println(str)
}
}()
wg.Wait()
}
$go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/sheldon/Gopath"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GO15VENDOREXPERIMENT="1"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
$go version
go version go1.6.2 linux/amd64
Im seeing the same. (Go 1.8)
package main
import (
"github.com/Workiva/go-datastructures/queue"
)
func main() {
rb := queue.NewRingBuffer(5)
rb.Get()
}
100% cpu usage for above
100% CPU
any news for this?
I need do this:
for rb.Len() == 0 { time.Sleep(50 * time.MillSecond) }
item, err := rb.Get()
another patch use chan notification, run better now
diff --git a/queue/ring.go b/queue/ring.go
index dfd0b53..8b40dc9 100644
--- a/queue/ring.go
+++ b/queue/ring.go
@@ -57,6 +57,7 @@ type RingBuffer struct {
_padding2 [8]uint64
mask, disposed uint64
_padding3 [8]uint64
+ condC chan bool
nodes nodes
}
@@ -67,6 +68,7 @@ func (rb *RingBuffer) init(size uint64) {
rb.nodes[i] = &node{position: i}
}
rb.mask = size - 1 // so we don't have to do this with every put/get operation
+ rb.condC = make(chan bool, 0)
}
// Put adds the provided item to the queue. If the queue is full, this
@@ -115,6 +117,12 @@ L:
n.data = item
atomic.StoreUint64(&n.position, pos+1)
+ if rb.Len() < 3 {
+ select {
+ case rb.condC <- true:
+ default:
+ }
+ }
return true, nil
}
@@ -146,6 +154,12 @@ L:
return nil, ErrDisposed
}
+ if rb.Len() == 0 {
+ select {
+ case <-rb.condC:
+ }
+ }
+
n = rb.nodes[pos&rb.mask]
seq := atomic.LoadUint64(&n.position)
switch dif := seq - (pos + 1); {
@@ -186,6 +200,10 @@ func (rb *RingBuffer) Cap() uint64 {
// queue will return an error.
func (rb *RingBuffer) Dispose() {
atomic.CompareAndSwapUint64(&rb.disposed, 0, 1)
+ select {
+ case rb.condC <- true:
+ default:
+ }
}
// IsDisposed will return a bool indicating if this queue has been
Get()
has 100% CPU usage because it uses busy waiting strategy when the buffer is empty.
Ideally we should add a new method which returns as soon as we find out the buffer is empty, and returns a boolean to allow users to decide what they want to do in this case (busy waiting, sleep, or something else). This is we do in Put()
and Offer()
.
For now users can workaround this issue by replacing Get()
with Poll(1)
, which returns (nil, ErrTimeout) after the buffer has been empty for 1ns.