| // Copyright 2011 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package runtime_test |
| |
| import ( |
| "math" |
| "runtime" |
| "sync/atomic" |
| "testing" |
| "time" |
| ) |
| |
| var stop = make(chan bool, 1) |
| |
| func perpetuumMobile() { |
| select { |
| case <-stop: |
| default: |
| go perpetuumMobile() |
| } |
| } |
| |
| func TestStopTheWorldDeadlock(t *testing.T) { |
| if testing.Short() { |
| t.Skip("skipping during short test") |
| } |
| maxprocs := runtime.GOMAXPROCS(3) |
| compl := make(chan bool, 2) |
| go func() { |
| for i := 0; i != 1000; i += 1 { |
| runtime.GC() |
| } |
| compl <- true |
| }() |
| go func() { |
| for i := 0; i != 1000; i += 1 { |
| runtime.GOMAXPROCS(3) |
| } |
| compl <- true |
| }() |
| go perpetuumMobile() |
| <-compl |
| <-compl |
| stop <- true |
| runtime.GOMAXPROCS(maxprocs) |
| } |
| |
| func TestYieldProgress(t *testing.T) { |
| testYieldProgress(t, false) |
| } |
| |
| func TestYieldLockedProgress(t *testing.T) { |
| testYieldProgress(t, true) |
| } |
| |
| func testYieldProgress(t *testing.T, locked bool) { |
| c := make(chan bool) |
| cack := make(chan bool) |
| go func() { |
| if locked { |
| runtime.LockOSThread() |
| } |
| for { |
| select { |
| case <-c: |
| cack <- true |
| return |
| default: |
| runtime.Gosched() |
| } |
| } |
| }() |
| time.Sleep(10 * time.Millisecond) |
| c <- true |
| <-cack |
| } |
| |
| func TestYieldLocked(t *testing.T) { |
| const N = 10 |
| c := make(chan bool) |
| go func() { |
| runtime.LockOSThread() |
| for i := 0; i < N; i++ { |
| runtime.Gosched() |
| time.Sleep(time.Millisecond) |
| } |
| c <- true |
| // runtime.UnlockOSThread() is deliberately omitted |
| }() |
| <-c |
| } |
| |
| func TestBlockLocked(t *testing.T) { |
| const N = 10 |
| c := make(chan bool) |
| go func() { |
| runtime.LockOSThread() |
| for i := 0; i < N; i++ { |
| c <- true |
| } |
| runtime.UnlockOSThread() |
| }() |
| for i := 0; i < N; i++ { |
| <-c |
| } |
| } |
| |
| func stackGrowthRecursive(i int) { |
| var pad [128]uint64 |
| if i != 0 && pad[0] == 0 { |
| stackGrowthRecursive(i - 1) |
| } |
| } |
| |
| func TestSchedLocalQueue(t *testing.T) { |
| runtime.TestSchedLocalQueue1() |
| } |
| |
| func TestSchedLocalQueueSteal(t *testing.T) { |
| runtime.TestSchedLocalQueueSteal1() |
| } |
| |
| func benchmarkStackGrowth(b *testing.B, rec int) { |
| const CallsPerSched = 1000 |
| procs := runtime.GOMAXPROCS(-1) |
| N := int32(b.N / CallsPerSched) |
| c := make(chan bool, procs) |
| for p := 0; p < procs; p++ { |
| go func() { |
| for atomic.AddInt32(&N, -1) >= 0 { |
| runtime.Gosched() |
| for g := 0; g < CallsPerSched; g++ { |
| stackGrowthRecursive(rec) |
| } |
| } |
| c <- true |
| }() |
| } |
| for p := 0; p < procs; p++ { |
| <-c |
| } |
| } |
| |
| func BenchmarkStackGrowth(b *testing.B) { |
| benchmarkStackGrowth(b, 10) |
| } |
| |
| func BenchmarkStackGrowthDeep(b *testing.B) { |
| benchmarkStackGrowth(b, 1024) |
| } |
| |
| func BenchmarkSyscall(b *testing.B) { |
| benchmarkSyscall(b, 0, 1) |
| } |
| |
| func BenchmarkSyscallWork(b *testing.B) { |
| benchmarkSyscall(b, 100, 1) |
| } |
| |
| func BenchmarkSyscallExcess(b *testing.B) { |
| benchmarkSyscall(b, 0, 4) |
| } |
| |
| func BenchmarkSyscallExcessWork(b *testing.B) { |
| benchmarkSyscall(b, 100, 4) |
| } |
| |
| func benchmarkSyscall(b *testing.B, work, excess int) { |
| const CallsPerSched = 1000 |
| procs := runtime.GOMAXPROCS(-1) * excess |
| N := int32(b.N / CallsPerSched) |
| c := make(chan bool, procs) |
| for p := 0; p < procs; p++ { |
| go func() { |
| foo := 42 |
| for atomic.AddInt32(&N, -1) >= 0 { |
| runtime.Gosched() |
| for g := 0; g < CallsPerSched; g++ { |
| runtime.Entersyscall() |
| for i := 0; i < work; i++ { |
| foo *= 2 |
| foo /= 2 |
| } |
| runtime.Exitsyscall() |
| } |
| } |
| c <- foo == 42 |
| }() |
| } |
| for p := 0; p < procs; p++ { |
| <-c |
| } |
| } |
| |
| func BenchmarkCreateGoroutines(b *testing.B) { |
| benchmarkCreateGoroutines(b, 1) |
| } |
| |
| func BenchmarkCreateGoroutinesParallel(b *testing.B) { |
| benchmarkCreateGoroutines(b, runtime.GOMAXPROCS(-1)) |
| } |
| |
| func benchmarkCreateGoroutines(b *testing.B, procs int) { |
| c := make(chan bool) |
| var f func(n int) |
| f = func(n int) { |
| if n == 0 { |
| c <- true |
| return |
| } |
| go f(n - 1) |
| } |
| for i := 0; i < procs; i++ { |
| go f(b.N / procs) |
| } |
| for i := 0; i < procs; i++ { |
| <-c |
| } |
| } |
| |
| type Matrix [][]float64 |
| |
| func BenchmarkMatmult(b *testing.B) { |
| b.StopTimer() |
| // matmult is O(N**3) but testing expects O(b.N), |
| // so we need to take cube root of b.N |
| n := int(math.Cbrt(float64(b.N))) + 1 |
| A := makeMatrix(n) |
| B := makeMatrix(n) |
| C := makeMatrix(n) |
| b.StartTimer() |
| matmult(nil, A, B, C, 0, n, 0, n, 0, n, 8) |
| } |
| |
| func makeMatrix(n int) Matrix { |
| m := make(Matrix, n) |
| for i := 0; i < n; i++ { |
| m[i] = make([]float64, n) |
| for j := 0; j < n; j++ { |
| m[i][j] = float64(i*n + j) |
| } |
| } |
| return m |
| } |
| |
| func matmult(done chan<- struct{}, A, B, C Matrix, i0, i1, j0, j1, k0, k1, threshold int) { |
| di := i1 - i0 |
| dj := j1 - j0 |
| dk := k1 - k0 |
| if di >= dj && di >= dk && di >= threshold { |
| // divide in two by y axis |
| mi := i0 + di/2 |
| done1 := make(chan struct{}, 1) |
| go matmult(done1, A, B, C, i0, mi, j0, j1, k0, k1, threshold) |
| matmult(nil, A, B, C, mi, i1, j0, j1, k0, k1, threshold) |
| <-done1 |
| } else if dj >= dk && dj >= threshold { |
| // divide in two by x axis |
| mj := j0 + dj/2 |
| done1 := make(chan struct{}, 1) |
| go matmult(done1, A, B, C, i0, i1, j0, mj, k0, k1, threshold) |
| matmult(nil, A, B, C, i0, i1, mj, j1, k0, k1, threshold) |
| <-done1 |
| } else if dk >= threshold { |
| // divide in two by "k" axis |
| // deliberately not parallel because of data races |
| mk := k0 + dk/2 |
| matmult(nil, A, B, C, i0, i1, j0, j1, k0, mk, threshold) |
| matmult(nil, A, B, C, i0, i1, j0, j1, mk, k1, threshold) |
| } else { |
| // the matrices are small enough, compute directly |
| for i := i0; i < i1; i++ { |
| for j := j0; j < j1; j++ { |
| for k := k0; k < k1; k++ { |
| C[i][j] += A[i][k] * B[k][j] |
| } |
| } |
| } |
| } |
| if done != nil { |
| done <- struct{}{} |
| } |
| } |