diff options
| author | Keith Randall <khr@golang.org> | 2014-12-08 10:11:08 -0800 |
|---|---|---|
| committer | Keith Randall <khr@golang.org> | 2014-12-08 19:20:12 +0000 |
| commit | 8eb8b40a4965c0bd5f96dfdfc5b037925f630c2d (patch) | |
| tree | e75d583cbdb939d973d7237c4c1a5163af031d80 /src/runtime/select.go | |
| parent | 006ceb2f1dd64e75134347ae9a73be397ff8a2ed (diff) | |
| download | go-8eb8b40a4965c0bd5f96dfdfc5b037925f630c2d.tar.xz | |
runtime: use doubly-linked lists for channel send/recv queues.
Avoids a potential O(n^2) performance problem when dequeueing
from very popular channels.
benchmark old ns/op new ns/op delta
BenchmarkChanPopular 2563782 627201 -75.54%
Change-Id: I231aaeafea0ecd93d27b268a0b2128530df3ddd6
Reviewed-on: https://go-review.googlesource.com/1200
Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/runtime/select.go')
| -rw-r--r-- | src/runtime/select.go | 46 |
1 files changed, 30 insertions, 16 deletions
diff --git a/src/runtime/select.go b/src/runtime/select.go index 5e5047bc10..63d436a9b6 100644 --- a/src/runtime/select.go +++ b/src/runtime/select.go @@ -389,6 +389,7 @@ loop: k.releasetime = sglist.releasetime } if sg == sglist { + // sg has already been dequeued by the G that woke us up. cas = k } else { c = k._chan @@ -624,23 +625,36 @@ func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) { return } -func (q *waitq) dequeueSudoG(s *sudog) { - var prevsgp *sudog - l := &q.first - for { - sgp := *l - if sgp == nil { +func (q *waitq) dequeueSudoG(sgp *sudog) { + x := sgp.prev + y := sgp.next + if x != nil { + if y != nil { + // middle of queue + x.next = y + y.prev = x + sgp.next = nil + sgp.prev = nil return } - if sgp == s { - *l = sgp.next - if q.last == sgp { - q.last = prevsgp - } - s.next = nil - return - } - l = &sgp.next - prevsgp = sgp + // end of queue + x.next = nil + q.last = x + sgp.prev = nil + return + } + if y != nil { + // start of queue + y.prev = nil + q.first = y + sgp.next = nil + return + } + + // x==y==nil. Either sgp is the only element in the queue, + // or it has already been removed. Use q.first to disambiguate. + if q.first == sgp { + q.first = nil + q.last = nil } } |
