Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 1 | // Copyright 2011 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package runtime_test |
| 6 | |
| 7 | import ( |
Dmitriy Vyukov | 72b09bd | 2013-03-01 00:41:45 +0200 | [diff] [blame] | 8 | "math" |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 9 | "runtime" |
Dmitriy Vyukov | c9152a8 | 2011-07-12 09:24:32 -0700 | [diff] [blame] | 10 | "sync/atomic" |
Dmitriy Vyukov | 4bb491b | 2013-06-15 16:06:28 +0400 | [diff] [blame] | 11 | "syscall" |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 12 | "testing" |
Dmitriy Vyukov | 6a82848 | 2013-02-15 00:02:12 +0400 | [diff] [blame] | 13 | "time" |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 14 | ) |
| 15 | |
Russ Cox | 781df13 | 2011-04-22 15:22:11 -0400 | [diff] [blame] | 16 | var stop = make(chan bool, 1) |
| 17 | |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 18 | func perpetuumMobile() { |
Russ Cox | 781df13 | 2011-04-22 15:22:11 -0400 | [diff] [blame] | 19 | select { |
| 20 | case <-stop: |
| 21 | default: |
| 22 | go perpetuumMobile() |
| 23 | } |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 24 | } |
| 25 | |
| 26 | func TestStopTheWorldDeadlock(t *testing.T) { |
Russ Cox | 4f7fd3c | 2011-04-23 10:03:51 -0400 | [diff] [blame] | 27 | if testing.Short() { |
Dave Cheney | 6a9e956 | 2013-01-24 17:32:10 +1100 | [diff] [blame] | 28 | t.Skip("skipping during short test") |
Russ Cox | 4f7fd3c | 2011-04-23 10:03:51 -0400 | [diff] [blame] | 29 | } |
Dmitriy Vyukov | 91cc1e6 | 2011-05-31 10:38:51 -0400 | [diff] [blame] | 30 | maxprocs := runtime.GOMAXPROCS(3) |
| 31 | compl := make(chan bool, 2) |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 32 | go func() { |
| 33 | for i := 0; i != 1000; i += 1 { |
| 34 | runtime.GC() |
| 35 | } |
Dmitriy Vyukov | 91cc1e6 | 2011-05-31 10:38:51 -0400 | [diff] [blame] | 36 | compl <- true |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 37 | }() |
| 38 | go func() { |
| 39 | for i := 0; i != 1000; i += 1 { |
| 40 | runtime.GOMAXPROCS(3) |
| 41 | } |
Dmitriy Vyukov | 91cc1e6 | 2011-05-31 10:38:51 -0400 | [diff] [blame] | 42 | compl <- true |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 43 | }() |
| 44 | go perpetuumMobile() |
| 45 | <-compl |
Dmitriy Vyukov | 91cc1e6 | 2011-05-31 10:38:51 -0400 | [diff] [blame] | 46 | <-compl |
Russ Cox | 781df13 | 2011-04-22 15:22:11 -0400 | [diff] [blame] | 47 | stop <- true |
Dmitriy Vyukov | 91cc1e6 | 2011-05-31 10:38:51 -0400 | [diff] [blame] | 48 | runtime.GOMAXPROCS(maxprocs) |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 49 | } |
Dmitriy Vyukov | c9152a8 | 2011-07-12 09:24:32 -0700 | [diff] [blame] | 50 | |
Dmitriy Vyukov | a92e11a | 2013-02-20 12:13:04 +0400 | [diff] [blame] | 51 | func TestYieldProgress(t *testing.T) { |
| 52 | testYieldProgress(t, false) |
| 53 | } |
| 54 | |
| 55 | func TestYieldLockedProgress(t *testing.T) { |
| 56 | testYieldProgress(t, true) |
| 57 | } |
| 58 | |
| 59 | func testYieldProgress(t *testing.T, locked bool) { |
| 60 | c := make(chan bool) |
| 61 | cack := make(chan bool) |
| 62 | go func() { |
| 63 | if locked { |
| 64 | runtime.LockOSThread() |
| 65 | } |
| 66 | for { |
| 67 | select { |
| 68 | case <-c: |
| 69 | cack <- true |
| 70 | return |
| 71 | default: |
| 72 | runtime.Gosched() |
| 73 | } |
| 74 | } |
| 75 | }() |
| 76 | time.Sleep(10 * time.Millisecond) |
| 77 | c <- true |
| 78 | <-cack |
| 79 | } |
| 80 | |
Dmitriy Vyukov | 6a82848 | 2013-02-15 00:02:12 +0400 | [diff] [blame] | 81 | func TestYieldLocked(t *testing.T) { |
| 82 | const N = 10 |
| 83 | c := make(chan bool) |
| 84 | go func() { |
| 85 | runtime.LockOSThread() |
| 86 | for i := 0; i < N; i++ { |
| 87 | runtime.Gosched() |
| 88 | time.Sleep(time.Millisecond) |
| 89 | } |
| 90 | c <- true |
| 91 | // runtime.UnlockOSThread() is deliberately omitted |
| 92 | }() |
| 93 | <-c |
| 94 | } |
| 95 | |
Dmitriy Vyukov | 32fef99 | 2013-07-11 15:57:36 -0400 | [diff] [blame^] | 96 | func TestGoroutineParallelism(t *testing.T) { |
| 97 | const P = 4 |
| 98 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P)) |
| 99 | for try := 0; try < 10; try++ { |
| 100 | done := make(chan bool) |
| 101 | x := uint32(0) |
| 102 | for p := 0; p < P; p++ { |
| 103 | // Test that all P goroutines are scheduled at the same time |
| 104 | go func(p int) { |
| 105 | for i := 0; i < 3; i++ { |
| 106 | expected := uint32(P*i + p) |
| 107 | for atomic.LoadUint32(&x) != expected { |
| 108 | } |
| 109 | atomic.StoreUint32(&x, expected+1) |
| 110 | } |
| 111 | done <- true |
| 112 | }(p) |
| 113 | } |
| 114 | for p := 0; p < P; p++ { |
| 115 | <-done |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
Dmitriy Vyukov | 6a82848 | 2013-02-15 00:02:12 +0400 | [diff] [blame] | 120 | func TestBlockLocked(t *testing.T) { |
| 121 | const N = 10 |
| 122 | c := make(chan bool) |
| 123 | go func() { |
| 124 | runtime.LockOSThread() |
| 125 | for i := 0; i < N; i++ { |
| 126 | c <- true |
| 127 | } |
| 128 | runtime.UnlockOSThread() |
| 129 | }() |
| 130 | for i := 0; i < N; i++ { |
| 131 | <-c |
| 132 | } |
| 133 | } |
| 134 | |
Dmitriy Vyukov | 4bb491b | 2013-06-15 16:06:28 +0400 | [diff] [blame] | 135 | func TestTimerFairness(t *testing.T) { |
| 136 | done := make(chan bool) |
| 137 | c := make(chan bool) |
| 138 | for i := 0; i < 2; i++ { |
| 139 | go func() { |
| 140 | for { |
| 141 | select { |
| 142 | case c <- true: |
| 143 | case <-done: |
| 144 | return |
| 145 | } |
| 146 | } |
| 147 | }() |
| 148 | } |
| 149 | |
| 150 | timer := time.After(20 * time.Millisecond) |
| 151 | for { |
| 152 | select { |
| 153 | case <-c: |
| 154 | case <-timer: |
| 155 | close(done) |
| 156 | return |
| 157 | } |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | func TestTimerFairness2(t *testing.T) { |
| 162 | done := make(chan bool) |
| 163 | c := make(chan bool) |
| 164 | for i := 0; i < 2; i++ { |
| 165 | go func() { |
| 166 | timer := time.After(20 * time.Millisecond) |
| 167 | var buf [1]byte |
| 168 | for { |
| 169 | syscall.Read(0, buf[0:0]) |
| 170 | select { |
| 171 | case c <- true: |
| 172 | case <-c: |
| 173 | case <-timer: |
| 174 | done <- true |
| 175 | return |
| 176 | } |
| 177 | } |
| 178 | }() |
| 179 | } |
| 180 | <-done |
| 181 | <-done |
| 182 | } |
| 183 | |
Dmitriy Vyukov | 1e112cd | 2013-06-28 17:52:17 +0400 | [diff] [blame] | 184 | // The function is used to test preemption at split stack checks. |
| 185 | // Declaring a var avoids inlining at the call site. |
| 186 | var preempt = func() int { |
| 187 | var a [128]int |
| 188 | sum := 0 |
| 189 | for _, v := range a { |
| 190 | sum += v |
| 191 | } |
| 192 | return sum |
| 193 | } |
| 194 | |
| 195 | func TestPreemptionGC(t *testing.T) { |
Russ Cox | 1184407 | 2013-07-01 18:10:03 -0400 | [diff] [blame] | 196 | t.Skip("preemption is disabled") |
Dmitriy Vyukov | 1e112cd | 2013-06-28 17:52:17 +0400 | [diff] [blame] | 197 | // Test that pending GC preempts running goroutines. |
| 198 | const P = 5 |
| 199 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1)) |
| 200 | var stop uint32 |
| 201 | for i := 0; i < P; i++ { |
| 202 | go func() { |
| 203 | for atomic.LoadUint32(&stop) == 0 { |
| 204 | preempt() |
| 205 | } |
| 206 | }() |
| 207 | } |
| 208 | for i := 0; i < 10; i++ { |
| 209 | runtime.Gosched() |
| 210 | runtime.GC() |
| 211 | } |
| 212 | atomic.StoreUint32(&stop, 1) |
| 213 | } |
| 214 | |
Dmitriy Vyukov | c9152a8 | 2011-07-12 09:24:32 -0700 | [diff] [blame] | 215 | func stackGrowthRecursive(i int) { |
| 216 | var pad [128]uint64 |
| 217 | if i != 0 && pad[0] == 0 { |
| 218 | stackGrowthRecursive(i - 1) |
| 219 | } |
| 220 | } |
| 221 | |
Dmitriy Vyukov | 353ce60 | 2013-02-23 08:48:02 +0400 | [diff] [blame] | 222 | func TestSchedLocalQueue(t *testing.T) { |
| 223 | runtime.TestSchedLocalQueue1() |
| 224 | } |
| 225 | |
| 226 | func TestSchedLocalQueueSteal(t *testing.T) { |
| 227 | runtime.TestSchedLocalQueueSteal1() |
| 228 | } |
| 229 | |
Dmitriy Vyukov | f82db7d | 2013-01-10 09:57:06 +0400 | [diff] [blame] | 230 | func benchmarkStackGrowth(b *testing.B, rec int) { |
Dmitriy Vyukov | c9152a8 | 2011-07-12 09:24:32 -0700 | [diff] [blame] | 231 | const CallsPerSched = 1000 |
| 232 | procs := runtime.GOMAXPROCS(-1) |
| 233 | N := int32(b.N / CallsPerSched) |
| 234 | c := make(chan bool, procs) |
| 235 | for p := 0; p < procs; p++ { |
| 236 | go func() { |
| 237 | for atomic.AddInt32(&N, -1) >= 0 { |
| 238 | runtime.Gosched() |
| 239 | for g := 0; g < CallsPerSched; g++ { |
Dmitriy Vyukov | f82db7d | 2013-01-10 09:57:06 +0400 | [diff] [blame] | 240 | stackGrowthRecursive(rec) |
Dmitriy Vyukov | c9152a8 | 2011-07-12 09:24:32 -0700 | [diff] [blame] | 241 | } |
| 242 | } |
| 243 | c <- true |
| 244 | }() |
| 245 | } |
| 246 | for p := 0; p < procs; p++ { |
| 247 | <-c |
| 248 | } |
| 249 | } |
Russ Cox | 025abd5 | 2011-07-19 11:01:17 -0400 | [diff] [blame] | 250 | |
Dmitriy Vyukov | f82db7d | 2013-01-10 09:57:06 +0400 | [diff] [blame] | 251 | func BenchmarkStackGrowth(b *testing.B) { |
| 252 | benchmarkStackGrowth(b, 10) |
| 253 | } |
| 254 | |
| 255 | func BenchmarkStackGrowthDeep(b *testing.B) { |
| 256 | benchmarkStackGrowth(b, 1024) |
| 257 | } |
| 258 | |
Russ Cox | 025abd5 | 2011-07-19 11:01:17 -0400 | [diff] [blame] | 259 | func BenchmarkSyscall(b *testing.B) { |
Dmitriy Vyukov | 38d4d3c | 2013-03-01 01:10:34 +0200 | [diff] [blame] | 260 | benchmarkSyscall(b, 0, 1) |
Russ Cox | 025abd5 | 2011-07-19 11:01:17 -0400 | [diff] [blame] | 261 | } |
| 262 | |
| 263 | func BenchmarkSyscallWork(b *testing.B) { |
Dmitriy Vyukov | 38d4d3c | 2013-03-01 01:10:34 +0200 | [diff] [blame] | 264 | benchmarkSyscall(b, 100, 1) |
| 265 | } |
| 266 | |
| 267 | func BenchmarkSyscallExcess(b *testing.B) { |
| 268 | benchmarkSyscall(b, 0, 4) |
| 269 | } |
| 270 | |
| 271 | func BenchmarkSyscallExcessWork(b *testing.B) { |
| 272 | benchmarkSyscall(b, 100, 4) |
| 273 | } |
| 274 | |
| 275 | func benchmarkSyscall(b *testing.B, work, excess int) { |
Russ Cox | 025abd5 | 2011-07-19 11:01:17 -0400 | [diff] [blame] | 276 | const CallsPerSched = 1000 |
Dmitriy Vyukov | 38d4d3c | 2013-03-01 01:10:34 +0200 | [diff] [blame] | 277 | procs := runtime.GOMAXPROCS(-1) * excess |
Russ Cox | 025abd5 | 2011-07-19 11:01:17 -0400 | [diff] [blame] | 278 | N := int32(b.N / CallsPerSched) |
| 279 | c := make(chan bool, procs) |
| 280 | for p := 0; p < procs; p++ { |
| 281 | go func() { |
| 282 | foo := 42 |
| 283 | for atomic.AddInt32(&N, -1) >= 0 { |
| 284 | runtime.Gosched() |
| 285 | for g := 0; g < CallsPerSched; g++ { |
| 286 | runtime.Entersyscall() |
Dmitriy Vyukov | 38d4d3c | 2013-03-01 01:10:34 +0200 | [diff] [blame] | 287 | for i := 0; i < work; i++ { |
Russ Cox | 025abd5 | 2011-07-19 11:01:17 -0400 | [diff] [blame] | 288 | foo *= 2 |
| 289 | foo /= 2 |
| 290 | } |
| 291 | runtime.Exitsyscall() |
| 292 | } |
| 293 | } |
| 294 | c <- foo == 42 |
| 295 | }() |
| 296 | } |
| 297 | for p := 0; p < procs; p++ { |
| 298 | <-c |
| 299 | } |
| 300 | } |
Dmitriy Vyukov | 5a5e698 | 2012-06-27 21:57:49 +0400 | [diff] [blame] | 301 | |
| 302 | func BenchmarkCreateGoroutines(b *testing.B) { |
| 303 | benchmarkCreateGoroutines(b, 1) |
| 304 | } |
| 305 | |
| 306 | func BenchmarkCreateGoroutinesParallel(b *testing.B) { |
| 307 | benchmarkCreateGoroutines(b, runtime.GOMAXPROCS(-1)) |
| 308 | } |
| 309 | |
| 310 | func benchmarkCreateGoroutines(b *testing.B, procs int) { |
| 311 | c := make(chan bool) |
| 312 | var f func(n int) |
| 313 | f = func(n int) { |
| 314 | if n == 0 { |
| 315 | c <- true |
| 316 | return |
| 317 | } |
| 318 | go f(n - 1) |
| 319 | } |
| 320 | for i := 0; i < procs; i++ { |
| 321 | go f(b.N / procs) |
| 322 | } |
| 323 | for i := 0; i < procs; i++ { |
| 324 | <-c |
| 325 | } |
| 326 | } |
Dmitriy Vyukov | 72b09bd | 2013-03-01 00:41:45 +0200 | [diff] [blame] | 327 | |
| 328 | type Matrix [][]float64 |
| 329 | |
| 330 | func BenchmarkMatmult(b *testing.B) { |
| 331 | b.StopTimer() |
| 332 | // matmult is O(N**3) but testing expects O(b.N), |
| 333 | // so we need to take cube root of b.N |
| 334 | n := int(math.Cbrt(float64(b.N))) + 1 |
| 335 | A := makeMatrix(n) |
| 336 | B := makeMatrix(n) |
| 337 | C := makeMatrix(n) |
| 338 | b.StartTimer() |
| 339 | matmult(nil, A, B, C, 0, n, 0, n, 0, n, 8) |
| 340 | } |
| 341 | |
| 342 | func makeMatrix(n int) Matrix { |
| 343 | m := make(Matrix, n) |
| 344 | for i := 0; i < n; i++ { |
| 345 | m[i] = make([]float64, n) |
| 346 | for j := 0; j < n; j++ { |
| 347 | m[i][j] = float64(i*n + j) |
| 348 | } |
| 349 | } |
| 350 | return m |
| 351 | } |
| 352 | |
| 353 | func matmult(done chan<- struct{}, A, B, C Matrix, i0, i1, j0, j1, k0, k1, threshold int) { |
| 354 | di := i1 - i0 |
| 355 | dj := j1 - j0 |
| 356 | dk := k1 - k0 |
| 357 | if di >= dj && di >= dk && di >= threshold { |
| 358 | // divide in two by y axis |
| 359 | mi := i0 + di/2 |
| 360 | done1 := make(chan struct{}, 1) |
| 361 | go matmult(done1, A, B, C, i0, mi, j0, j1, k0, k1, threshold) |
| 362 | matmult(nil, A, B, C, mi, i1, j0, j1, k0, k1, threshold) |
| 363 | <-done1 |
| 364 | } else if dj >= dk && dj >= threshold { |
| 365 | // divide in two by x axis |
| 366 | mj := j0 + dj/2 |
| 367 | done1 := make(chan struct{}, 1) |
| 368 | go matmult(done1, A, B, C, i0, i1, j0, mj, k0, k1, threshold) |
| 369 | matmult(nil, A, B, C, i0, i1, mj, j1, k0, k1, threshold) |
| 370 | <-done1 |
| 371 | } else if dk >= threshold { |
| 372 | // divide in two by "k" axis |
| 373 | // deliberately not parallel because of data races |
| 374 | mk := k0 + dk/2 |
| 375 | matmult(nil, A, B, C, i0, i1, j0, j1, k0, mk, threshold) |
| 376 | matmult(nil, A, B, C, i0, i1, j0, j1, mk, k1, threshold) |
| 377 | } else { |
| 378 | // the matrices are small enough, compute directly |
| 379 | for i := i0; i < i1; i++ { |
| 380 | for j := j0; j < j1; j++ { |
| 381 | for k := k0; k < k1; k++ { |
| 382 | C[i][j] += A[i][k] * B[k][j] |
| 383 | } |
| 384 | } |
| 385 | } |
| 386 | } |
| 387 | if done != nil { |
| 388 | done <- struct{}{} |
| 389 | } |
| 390 | } |