Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RingBuffer: 100% cpu usage #150

Open
sheldonlyr opened this issue Jul 18, 2016 · 7 comments
Open

RingBuffer: 100% cpu usage #150

sheldonlyr opened this issue Jul 18, 2016 · 7 comments

Comments

@sheldonlyr
Copy link

sheldonlyr commented Jul 18, 2016

$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()


}

@sheldonlyr
Copy link
Author

$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"

@sheldonlyr
Copy link
Author

$go version
go version go1.6.2 linux/amd64

@sheldonlyr sheldonlyr changed the title RingBuffer: %100 cpu usage RingBuffer: 100% cpu usage Jul 18, 2016
@riaan53
Copy link

riaan53 commented Jan 31, 2017

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

@kitech
Copy link

kitech commented Jun 13, 2017

100% CPU

any news for this?

@kitech
Copy link

kitech commented Jun 14, 2017

I need do this:

for rb.Len() == 0 { time.Sleep(50 * time.MillSecond) }
item, err := rb.Get()

@kitech
Copy link

kitech commented Jan 20, 2019

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

@chrisxue815
Copy link
Contributor

chrisxue815 commented Aug 29, 2019

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants