mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: remove unnecessary wakeups of worker threads
Currently we wake up new worker threads whenever we pass
through the scheduler with nmspinning==0. This leads to
lots of unnecessary thread wake ups.
Instead let only spinning threads wake up new spinning threads.
For the following program:
package main
import "runtime"
func main() {
for i := 0; i < 1e7; i++ {
runtime.Gosched()
}
}
Before:
$ time ./test
real 0m4.278s
user 0m7.634s
sys 0m1.423s
$ strace -c ./test
% time seconds usecs/call calls errors syscall
99.93 9.314936 3 2685009 17536 futex
After:
$ time ./test
real 0m1.200s
user 0m1.181s
sys 0m0.024s
$ strace -c ./test
% time seconds usecs/call calls errors syscall
3.11 0.000049 25 2 futex
Fixes #13527
Change-Id: Ia1f5bf8a896dcc25d8b04beb1f4317aa9ff16f74
Reviewed-on: https://go-review.googlesource.com/17540
Reviewed-by: Austin Clements <austin@google.com>
Run-TryBot: Dmitry Vyukov <dvyukov@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
8545ea9cee
commit
fb6f8a96f2
3 changed files with 181 additions and 37 deletions
|
|
@ -6,6 +6,7 @@ package runtime_test
|
|||
|
||||
import (
|
||||
"math"
|
||||
"net"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
|
|
@ -132,6 +133,79 @@ func TestGoroutineParallelism(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test that all runnable goroutines are scheduled at the same time.
|
||||
func TestGoroutineParallelism2(t *testing.T) {
|
||||
//testGoroutineParallelism2(t, false, false)
|
||||
testGoroutineParallelism2(t, true, false)
|
||||
testGoroutineParallelism2(t, false, true)
|
||||
testGoroutineParallelism2(t, true, true)
|
||||
}
|
||||
|
||||
func testGoroutineParallelism2(t *testing.T, load, netpoll bool) {
|
||||
if runtime.NumCPU() == 1 {
|
||||
// Takes too long, too easy to deadlock, etc.
|
||||
t.Skip("skipping on uniprocessor")
|
||||
}
|
||||
P := 4
|
||||
N := 10
|
||||
if testing.Short() {
|
||||
N = 3
|
||||
}
|
||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
|
||||
// If runtime triggers a forced GC during this test then it will deadlock,
|
||||
// since the goroutines can't be stopped/preempted.
|
||||
// Disable GC for this test (see issue #10958).
|
||||
defer debug.SetGCPercent(debug.SetGCPercent(-1))
|
||||
for try := 0; try < N; try++ {
|
||||
if load {
|
||||
// Create P goroutines and wait until they all run.
|
||||
// When we run the actual test below, worker threads
|
||||
// running the goroutines will start parking.
|
||||
done := make(chan bool)
|
||||
x := uint32(0)
|
||||
for p := 0; p < P; p++ {
|
||||
go func() {
|
||||
if atomic.AddUint32(&x, 1) == uint32(P) {
|
||||
done <- true
|
||||
return
|
||||
}
|
||||
for atomic.LoadUint32(&x) != uint32(P) {
|
||||
}
|
||||
}()
|
||||
}
|
||||
<-done
|
||||
}
|
||||
if netpoll {
|
||||
// Enable netpoller, affects schedler behavior.
|
||||
ln, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
defer ln.Close() // yup, defer in a loop
|
||||
}
|
||||
}
|
||||
done := make(chan bool)
|
||||
x := uint32(0)
|
||||
// Spawn P goroutines in a nested fashion just to differ from TestGoroutineParallelism.
|
||||
for p := 0; p < P/2; p++ {
|
||||
go func(p int) {
|
||||
for p2 := 0; p2 < 2; p2++ {
|
||||
go func(p2 int) {
|
||||
for i := 0; i < 3; i++ {
|
||||
expected := uint32(P*i + p*2 + p2)
|
||||
for atomic.LoadUint32(&x) != expected {
|
||||
}
|
||||
atomic.StoreUint32(&x, expected+1)
|
||||
}
|
||||
done <- true
|
||||
}(p2)
|
||||
}
|
||||
}(p)
|
||||
}
|
||||
for p := 0; p < P; p++ {
|
||||
<-done
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockLocked(t *testing.T) {
|
||||
const N = 10
|
||||
c := make(chan bool)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue