diff options
author | Dmitry Vyukov <dvyukov@google.com> | 2015-12-08 15:11:27 +0100 |
---|---|---|
committer | Dmitry Vyukov <dvyukov@google.com> | 2015-12-11 11:31:12 +0000 |
commit | fb6f8a96f24f6b30e99cc77d78bc0194ffec7a41 (patch) | |
tree | 4f407ee25a38172dacb825259c48a435549c8c23 /src/runtime/proc_test.go | |
parent | 8545ea9cee087fd0fbac41bba7616d2fc4f2bc19 (diff) | |
download | go-fb6f8a96f24f6b30e99cc77d78bc0194ffec7a41.tar.gz go-fb6f8a96f24f6b30e99cc77d78bc0194ffec7a41.zip |
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>
Diffstat (limited to 'src/runtime/proc_test.go')
-rw-r--r-- | src/runtime/proc_test.go | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go index 2be103e3a6..c0213086b3 100644 --- a/src/runtime/proc_test.go +++ b/src/runtime/proc_test.go @@ -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) |