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" |
Austin Clements | f2c3957 | 2015-05-26 14:32:24 -0400 | [diff] [blame] | 10 | "runtime/debug" |
Dmitry Vyukov | 0e80b2e | 2015-01-19 22:59:58 +0300 | [diff] [blame] | 11 | "sync" |
Dmitriy Vyukov | c9152a8 | 2011-07-12 09:24:32 -0700 | [diff] [blame] | 12 | "sync/atomic" |
Dmitriy Vyukov | 4bb491b | 2013-06-15 16:06:28 +0400 | [diff] [blame] | 13 | "syscall" |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 14 | "testing" |
Dmitriy Vyukov | 6a82848 | 2013-02-15 00:02:12 +0400 | [diff] [blame] | 15 | "time" |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 16 | ) |
| 17 | |
Russ Cox | 781df13 | 2011-04-22 15:22:11 -0400 | [diff] [blame] | 18 | var stop = make(chan bool, 1) |
| 19 | |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 20 | func perpetuumMobile() { |
Russ Cox | 781df13 | 2011-04-22 15:22:11 -0400 | [diff] [blame] | 21 | select { |
| 22 | case <-stop: |
| 23 | default: |
| 24 | go perpetuumMobile() |
| 25 | } |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 26 | } |
| 27 | |
| 28 | func TestStopTheWorldDeadlock(t *testing.T) { |
Russ Cox | 4f7fd3c | 2011-04-23 10:03:51 -0400 | [diff] [blame] | 29 | if testing.Short() { |
Dave Cheney | 6a9e956 | 2013-01-24 17:32:10 +1100 | [diff] [blame] | 30 | t.Skip("skipping during short test") |
Russ Cox | 4f7fd3c | 2011-04-23 10:03:51 -0400 | [diff] [blame] | 31 | } |
Dmitriy Vyukov | 91cc1e6 | 2011-05-31 10:38:51 -0400 | [diff] [blame] | 32 | maxprocs := runtime.GOMAXPROCS(3) |
| 33 | compl := make(chan bool, 2) |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 34 | go func() { |
| 35 | for i := 0; i != 1000; i += 1 { |
| 36 | runtime.GC() |
| 37 | } |
Dmitriy Vyukov | 91cc1e6 | 2011-05-31 10:38:51 -0400 | [diff] [blame] | 38 | compl <- true |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 39 | }() |
| 40 | go func() { |
| 41 | for i := 0; i != 1000; i += 1 { |
| 42 | runtime.GOMAXPROCS(3) |
| 43 | } |
Dmitriy Vyukov | 91cc1e6 | 2011-05-31 10:38:51 -0400 | [diff] [blame] | 44 | compl <- true |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 45 | }() |
| 46 | go perpetuumMobile() |
| 47 | <-compl |
Dmitriy Vyukov | 91cc1e6 | 2011-05-31 10:38:51 -0400 | [diff] [blame] | 48 | <-compl |
Russ Cox | 781df13 | 2011-04-22 15:22:11 -0400 | [diff] [blame] | 49 | stop <- true |
Dmitriy Vyukov | 91cc1e6 | 2011-05-31 10:38:51 -0400 | [diff] [blame] | 50 | runtime.GOMAXPROCS(maxprocs) |
Dmitriy Vyukov | 29d78f1 | 2011-04-21 12:09:25 -0400 | [diff] [blame] | 51 | } |
Dmitriy Vyukov | c9152a8 | 2011-07-12 09:24:32 -0700 | [diff] [blame] | 52 | |
Dmitriy Vyukov | a92e11a | 2013-02-20 12:13:04 +0400 | [diff] [blame] | 53 | func TestYieldProgress(t *testing.T) { |
| 54 | testYieldProgress(t, false) |
| 55 | } |
| 56 | |
| 57 | func TestYieldLockedProgress(t *testing.T) { |
| 58 | testYieldProgress(t, true) |
| 59 | } |
| 60 | |
| 61 | func testYieldProgress(t *testing.T, locked bool) { |
| 62 | c := make(chan bool) |
| 63 | cack := make(chan bool) |
| 64 | go func() { |
| 65 | if locked { |
| 66 | runtime.LockOSThread() |
| 67 | } |
| 68 | for { |
| 69 | select { |
| 70 | case <-c: |
| 71 | cack <- true |
| 72 | return |
| 73 | default: |
| 74 | runtime.Gosched() |
| 75 | } |
| 76 | } |
| 77 | }() |
| 78 | time.Sleep(10 * time.Millisecond) |
| 79 | c <- true |
| 80 | <-cack |
| 81 | } |
| 82 | |
Dmitriy Vyukov | 6a82848 | 2013-02-15 00:02:12 +0400 | [diff] [blame] | 83 | func TestYieldLocked(t *testing.T) { |
| 84 | const N = 10 |
| 85 | c := make(chan bool) |
| 86 | go func() { |
| 87 | runtime.LockOSThread() |
| 88 | for i := 0; i < N; i++ { |
| 89 | runtime.Gosched() |
| 90 | time.Sleep(time.Millisecond) |
| 91 | } |
| 92 | c <- true |
| 93 | // runtime.UnlockOSThread() is deliberately omitted |
| 94 | }() |
| 95 | <-c |
| 96 | } |
| 97 | |
Dmitriy Vyukov | 32fef99 | 2013-07-11 15:57:36 -0400 | [diff] [blame] | 98 | func TestGoroutineParallelism(t *testing.T) { |
Russ Cox | 4a4eba9 | 2015-07-20 20:30:41 -0400 | [diff] [blame] | 99 | if runtime.NumCPU() == 1 { |
| 100 | // Takes too long, too easy to deadlock, etc. |
| 101 | t.Skip("skipping on uniprocessor") |
| 102 | } |
Dmitriy Vyukov | d8bbbd2 | 2013-08-01 18:25:36 +0400 | [diff] [blame] | 103 | P := 4 |
| 104 | N := 10 |
| 105 | if testing.Short() { |
| 106 | P = 3 |
| 107 | N = 3 |
| 108 | } |
Dmitriy Vyukov | 32fef99 | 2013-07-11 15:57:36 -0400 | [diff] [blame] | 109 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P)) |
Dmitriy Vyukov | 387c1c6 | 2014-07-15 10:30:12 +0400 | [diff] [blame] | 110 | // If runtime triggers a forced GC during this test then it will deadlock, |
| 111 | // since the goroutines can't be stopped/preempted. |
Austin Clements | f2c3957 | 2015-05-26 14:32:24 -0400 | [diff] [blame] | 112 | // Disable GC for this test (see issue #10958). |
| 113 | defer debug.SetGCPercent(debug.SetGCPercent(-1)) |
Dmitriy Vyukov | d8bbbd2 | 2013-08-01 18:25:36 +0400 | [diff] [blame] | 114 | for try := 0; try < N; try++ { |
Dmitriy Vyukov | 32fef99 | 2013-07-11 15:57:36 -0400 | [diff] [blame] | 115 | done := make(chan bool) |
| 116 | x := uint32(0) |
| 117 | for p := 0; p < P; p++ { |
| 118 | // Test that all P goroutines are scheduled at the same time |
| 119 | go func(p int) { |
| 120 | for i := 0; i < 3; i++ { |
| 121 | expected := uint32(P*i + p) |
| 122 | for atomic.LoadUint32(&x) != expected { |
| 123 | } |
| 124 | atomic.StoreUint32(&x, expected+1) |
| 125 | } |
| 126 | done <- true |
| 127 | }(p) |
| 128 | } |
| 129 | for p := 0; p < P; p++ { |
| 130 | <-done |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | |
Dmitriy Vyukov | 6a82848 | 2013-02-15 00:02:12 +0400 | [diff] [blame] | 135 | func TestBlockLocked(t *testing.T) { |
| 136 | const N = 10 |
| 137 | c := make(chan bool) |
| 138 | go func() { |
| 139 | runtime.LockOSThread() |
| 140 | for i := 0; i < N; i++ { |
| 141 | c <- true |
| 142 | } |
| 143 | runtime.UnlockOSThread() |
| 144 | }() |
| 145 | for i := 0; i < N; i++ { |
| 146 | <-c |
| 147 | } |
| 148 | } |
| 149 | |
Dmitriy Vyukov | 4bb491b | 2013-06-15 16:06:28 +0400 | [diff] [blame] | 150 | func TestTimerFairness(t *testing.T) { |
| 151 | done := make(chan bool) |
| 152 | c := make(chan bool) |
| 153 | for i := 0; i < 2; i++ { |
| 154 | go func() { |
| 155 | for { |
| 156 | select { |
| 157 | case c <- true: |
| 158 | case <-done: |
| 159 | return |
| 160 | } |
| 161 | } |
| 162 | }() |
| 163 | } |
| 164 | |
| 165 | timer := time.After(20 * time.Millisecond) |
| 166 | for { |
| 167 | select { |
| 168 | case <-c: |
| 169 | case <-timer: |
| 170 | close(done) |
| 171 | return |
| 172 | } |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | func TestTimerFairness2(t *testing.T) { |
| 177 | done := make(chan bool) |
| 178 | c := make(chan bool) |
| 179 | for i := 0; i < 2; i++ { |
| 180 | go func() { |
| 181 | timer := time.After(20 * time.Millisecond) |
| 182 | var buf [1]byte |
| 183 | for { |
| 184 | syscall.Read(0, buf[0:0]) |
| 185 | select { |
| 186 | case c <- true: |
| 187 | case <-c: |
| 188 | case <-timer: |
| 189 | done <- true |
| 190 | return |
| 191 | } |
| 192 | } |
| 193 | }() |
| 194 | } |
| 195 | <-done |
| 196 | <-done |
| 197 | } |
| 198 | |
Dmitriy Vyukov | 1e112cd | 2013-06-28 17:52:17 +0400 | [diff] [blame] | 199 | // The function is used to test preemption at split stack checks. |
| 200 | // Declaring a var avoids inlining at the call site. |
| 201 | var preempt = func() int { |
| 202 | var a [128]int |
| 203 | sum := 0 |
| 204 | for _, v := range a { |
| 205 | sum += v |
| 206 | } |
| 207 | return sum |
| 208 | } |
| 209 | |
Dmitriy Vyukov | bc31bcc | 2013-07-19 01:22:26 +0400 | [diff] [blame] | 210 | func TestPreemption(t *testing.T) { |
Dmitriy Vyukov | bc31bcc | 2013-07-19 01:22:26 +0400 | [diff] [blame] | 211 | // Test that goroutines are preempted at function calls. |
Dmitriy Vyukov | d8bbbd2 | 2013-08-01 18:25:36 +0400 | [diff] [blame] | 212 | N := 5 |
| 213 | if testing.Short() { |
| 214 | N = 2 |
| 215 | } |
Dmitriy Vyukov | bc31bcc | 2013-07-19 01:22:26 +0400 | [diff] [blame] | 216 | c := make(chan bool) |
| 217 | var x uint32 |
| 218 | for g := 0; g < 2; g++ { |
| 219 | go func(g int) { |
| 220 | for i := 0; i < N; i++ { |
| 221 | for atomic.LoadUint32(&x) != uint32(g) { |
| 222 | preempt() |
| 223 | } |
| 224 | atomic.StoreUint32(&x, uint32(1-g)) |
| 225 | } |
| 226 | c <- true |
| 227 | }(g) |
| 228 | } |
| 229 | <-c |
| 230 | <-c |
| 231 | } |
| 232 | |
Dmitriy Vyukov | 1e112cd | 2013-06-28 17:52:17 +0400 | [diff] [blame] | 233 | func TestPreemptionGC(t *testing.T) { |
| 234 | // Test that pending GC preempts running goroutines. |
Dmitriy Vyukov | d8bbbd2 | 2013-08-01 18:25:36 +0400 | [diff] [blame] | 235 | P := 5 |
| 236 | N := 10 |
| 237 | if testing.Short() { |
| 238 | P = 3 |
| 239 | N = 2 |
| 240 | } |
Dmitriy Vyukov | 1e112cd | 2013-06-28 17:52:17 +0400 | [diff] [blame] | 241 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1)) |
| 242 | var stop uint32 |
| 243 | for i := 0; i < P; i++ { |
| 244 | go func() { |
| 245 | for atomic.LoadUint32(&stop) == 0 { |
| 246 | preempt() |
| 247 | } |
| 248 | }() |
| 249 | } |
Dmitriy Vyukov | d8bbbd2 | 2013-08-01 18:25:36 +0400 | [diff] [blame] | 250 | for i := 0; i < N; i++ { |
Dmitriy Vyukov | 1e112cd | 2013-06-28 17:52:17 +0400 | [diff] [blame] | 251 | runtime.Gosched() |
| 252 | runtime.GC() |
| 253 | } |
| 254 | atomic.StoreUint32(&stop, 1) |
| 255 | } |
| 256 | |
Dmitriy Vyukov | 90eca36 | 2014-01-21 10:24:42 +0400 | [diff] [blame] | 257 | func TestGCFairness(t *testing.T) { |
| 258 | output := executeTest(t, testGCFairnessSource, nil) |
| 259 | want := "OK\n" |
| 260 | if output != want { |
| 261 | t.Fatalf("want %s, got %s\n", want, output) |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | const testGCFairnessSource = ` |
| 266 | package main |
| 267 | |
| 268 | import ( |
| 269 | "fmt" |
| 270 | "os" |
| 271 | "runtime" |
| 272 | "time" |
| 273 | ) |
| 274 | |
| 275 | func main() { |
| 276 | runtime.GOMAXPROCS(1) |
| 277 | f, err := os.Open("/dev/null") |
| 278 | if os.IsNotExist(err) { |
| 279 | // This test tests what it is intended to test only if writes are fast. |
| 280 | // If there is no /dev/null, we just don't execute the test. |
Dmitriy Vyukov | d76a1e5 | 2014-01-21 10:44:08 +0400 | [diff] [blame] | 281 | fmt.Println("OK") |
Dmitriy Vyukov | 90eca36 | 2014-01-21 10:24:42 +0400 | [diff] [blame] | 282 | return |
| 283 | } |
| 284 | if err != nil { |
| 285 | fmt.Println(err) |
| 286 | os.Exit(1) |
| 287 | } |
| 288 | for i := 0; i < 2; i++ { |
| 289 | go func() { |
| 290 | for { |
| 291 | f.Write([]byte(".")) |
| 292 | } |
| 293 | }() |
| 294 | } |
| 295 | time.Sleep(10 * time.Millisecond) |
| 296 | fmt.Println("OK") |
| 297 | } |
| 298 | ` |
| 299 | |
Austin Clements | e870f06 | 2015-04-22 14:42:26 -0400 | [diff] [blame] | 300 | func TestPingPongHog(t *testing.T) { |
| 301 | if testing.Short() { |
| 302 | t.Skip("skipping in -short mode") |
| 303 | } |
| 304 | |
| 305 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) |
| 306 | done := make(chan bool) |
| 307 | hogChan, lightChan := make(chan bool), make(chan bool) |
| 308 | hogCount, lightCount := 0, 0 |
| 309 | |
| 310 | run := func(limit int, counter *int, wake chan bool) { |
| 311 | for { |
| 312 | select { |
| 313 | case <-done: |
| 314 | return |
| 315 | |
| 316 | case <-wake: |
| 317 | for i := 0; i < limit; i++ { |
| 318 | *counter++ |
| 319 | } |
| 320 | wake <- true |
| 321 | } |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | // Start two co-scheduled hog goroutines. |
| 326 | for i := 0; i < 2; i++ { |
| 327 | go run(1e6, &hogCount, hogChan) |
| 328 | } |
| 329 | |
| 330 | // Start two co-scheduled light goroutines. |
| 331 | for i := 0; i < 2; i++ { |
| 332 | go run(1e3, &lightCount, lightChan) |
| 333 | } |
| 334 | |
| 335 | // Start goroutine pairs and wait for a few preemption rounds. |
| 336 | hogChan <- true |
| 337 | lightChan <- true |
| 338 | time.Sleep(100 * time.Millisecond) |
| 339 | close(done) |
| 340 | <-hogChan |
| 341 | <-lightChan |
| 342 | |
| 343 | // Check that hogCount and lightCount are within a factor of |
| 344 | // 2, which indicates that both pairs of goroutines handed off |
| 345 | // the P within a time-slice to their buddy. |
| 346 | if hogCount > lightCount*2 || lightCount > hogCount*2 { |
| 347 | t.Fatalf("want hogCount/lightCount in [0.5, 2]; got %d/%d = %g", hogCount, lightCount, float64(hogCount)/float64(lightCount)) |
| 348 | } |
| 349 | } |
| 350 | |
Austin Clements | da0e37f | 2015-04-22 16:38:04 -0400 | [diff] [blame] | 351 | func BenchmarkPingPongHog(b *testing.B) { |
| 352 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) |
| 353 | |
| 354 | // Create a CPU hog |
| 355 | stop, done := make(chan bool), make(chan bool) |
| 356 | go func() { |
| 357 | for { |
| 358 | select { |
| 359 | case <-stop: |
| 360 | done <- true |
| 361 | return |
| 362 | default: |
| 363 | } |
| 364 | } |
| 365 | }() |
| 366 | |
| 367 | // Ping-pong b.N times |
| 368 | ping, pong := make(chan bool), make(chan bool) |
| 369 | go func() { |
| 370 | for j := 0; j < b.N; j++ { |
| 371 | pong <- <-ping |
| 372 | } |
| 373 | close(stop) |
Russ Cox | 42da270 | 2015-04-27 16:08:11 -0400 | [diff] [blame] | 374 | done <- true |
Austin Clements | da0e37f | 2015-04-22 16:38:04 -0400 | [diff] [blame] | 375 | }() |
| 376 | go func() { |
| 377 | for i := 0; i < b.N; i++ { |
| 378 | ping <- <-pong |
| 379 | } |
Russ Cox | 42da270 | 2015-04-27 16:08:11 -0400 | [diff] [blame] | 380 | done <- true |
Austin Clements | da0e37f | 2015-04-22 16:38:04 -0400 | [diff] [blame] | 381 | }() |
| 382 | b.ResetTimer() |
| 383 | ping <- true // Start ping-pong |
| 384 | <-stop |
| 385 | b.StopTimer() |
| 386 | <-ping // Let last ponger exit |
Russ Cox | 42da270 | 2015-04-27 16:08:11 -0400 | [diff] [blame] | 387 | <-done // Make sure goroutines exit |
| 388 | <-done |
| 389 | <-done |
Austin Clements | da0e37f | 2015-04-22 16:38:04 -0400 | [diff] [blame] | 390 | } |
| 391 | |
Dmitriy Vyukov | c9152a8 | 2011-07-12 09:24:32 -0700 | [diff] [blame] | 392 | func stackGrowthRecursive(i int) { |
| 393 | var pad [128]uint64 |
| 394 | if i != 0 && pad[0] == 0 { |
| 395 | stackGrowthRecursive(i - 1) |
| 396 | } |
| 397 | } |
| 398 | |
Russ Cox | 031c107 | 2013-07-12 12:12:56 -0400 | [diff] [blame] | 399 | func TestPreemptSplitBig(t *testing.T) { |
| 400 | if testing.Short() { |
| 401 | t.Skip("skipping in -short mode") |
| 402 | } |
| 403 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) |
| 404 | stop := make(chan int) |
| 405 | go big(stop) |
| 406 | for i := 0; i < 3; i++ { |
Dmitriy Vyukov | 0a86b4d | 2013-07-16 01:02:42 +0400 | [diff] [blame] | 407 | time.Sleep(10 * time.Microsecond) // let big start running |
Russ Cox | 031c107 | 2013-07-12 12:12:56 -0400 | [diff] [blame] | 408 | runtime.GC() |
| 409 | } |
| 410 | close(stop) |
| 411 | } |
| 412 | |
| 413 | func big(stop chan int) int { |
| 414 | n := 0 |
| 415 | for { |
| 416 | // delay so that gc is sure to have asked for a preemption |
Dmitriy Vyukov | 0a86b4d | 2013-07-16 01:02:42 +0400 | [diff] [blame] | 417 | for i := 0; i < 1e9; i++ { |
Russ Cox | 031c107 | 2013-07-12 12:12:56 -0400 | [diff] [blame] | 418 | n++ |
| 419 | } |
| 420 | |
| 421 | // call bigframe, which used to miss the preemption in its prologue. |
| 422 | bigframe(stop) |
| 423 | |
| 424 | // check if we've been asked to stop. |
| 425 | select { |
| 426 | case <-stop: |
| 427 | return n |
| 428 | } |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | func bigframe(stop chan int) int { |
| 433 | // not splitting the stack will overflow. |
| 434 | // small will notice that it needs a stack split and will |
| 435 | // catch the overflow. |
| 436 | var x [8192]byte |
| 437 | return small(stop, &x) |
| 438 | } |
| 439 | |
| 440 | func small(stop chan int, x *[8192]byte) int { |
| 441 | for i := range x { |
| 442 | x[i] = byte(i) |
| 443 | } |
| 444 | sum := 0 |
| 445 | for i := range x { |
| 446 | sum += int(x[i]) |
| 447 | } |
| 448 | |
| 449 | // keep small from being a leaf function, which might |
| 450 | // make it not do any stack check at all. |
| 451 | nonleaf(stop) |
| 452 | |
| 453 | return sum |
| 454 | } |
| 455 | |
| 456 | func nonleaf(stop chan int) bool { |
| 457 | // do something that won't be inlined: |
| 458 | select { |
| 459 | case <-stop: |
| 460 | return true |
| 461 | default: |
| 462 | return false |
| 463 | } |
| 464 | } |
| 465 | |
Dmitriy Vyukov | 353ce60 | 2013-02-23 08:48:02 +0400 | [diff] [blame] | 466 | func TestSchedLocalQueue(t *testing.T) { |
Keith Randall | dbed4e9 | 2014-09-06 10:07:23 -0700 | [diff] [blame] | 467 | runtime.RunSchedLocalQueueTest() |
Dmitriy Vyukov | 353ce60 | 2013-02-23 08:48:02 +0400 | [diff] [blame] | 468 | } |
| 469 | |
| 470 | func TestSchedLocalQueueSteal(t *testing.T) { |
Keith Randall | dbed4e9 | 2014-09-06 10:07:23 -0700 | [diff] [blame] | 471 | runtime.RunSchedLocalQueueStealTest() |
Dmitriy Vyukov | 353ce60 | 2013-02-23 08:48:02 +0400 | [diff] [blame] | 472 | } |
| 473 | |
Dmitriy Vyukov | f82db7d | 2013-01-10 09:57:06 +0400 | [diff] [blame] | 474 | func benchmarkStackGrowth(b *testing.B, rec int) { |
Dmitriy Vyukov | 69257d1 | 2014-02-24 20:50:12 +0400 | [diff] [blame] | 475 | b.RunParallel(func(pb *testing.PB) { |
| 476 | for pb.Next() { |
| 477 | stackGrowthRecursive(rec) |
| 478 | } |
| 479 | }) |
Dmitriy Vyukov | c9152a8 | 2011-07-12 09:24:32 -0700 | [diff] [blame] | 480 | } |
Russ Cox | 025abd5 | 2011-07-19 11:01:17 -0400 | [diff] [blame] | 481 | |
Dmitriy Vyukov | f82db7d | 2013-01-10 09:57:06 +0400 | [diff] [blame] | 482 | func BenchmarkStackGrowth(b *testing.B) { |
| 483 | benchmarkStackGrowth(b, 10) |
| 484 | } |
| 485 | |
| 486 | func BenchmarkStackGrowthDeep(b *testing.B) { |
| 487 | benchmarkStackGrowth(b, 1024) |
| 488 | } |
| 489 | |
Dmitriy Vyukov | 5a5e698 | 2012-06-27 21:57:49 +0400 | [diff] [blame] | 490 | func BenchmarkCreateGoroutines(b *testing.B) { |
| 491 | benchmarkCreateGoroutines(b, 1) |
| 492 | } |
| 493 | |
| 494 | func BenchmarkCreateGoroutinesParallel(b *testing.B) { |
| 495 | benchmarkCreateGoroutines(b, runtime.GOMAXPROCS(-1)) |
| 496 | } |
| 497 | |
| 498 | func benchmarkCreateGoroutines(b *testing.B, procs int) { |
| 499 | c := make(chan bool) |
| 500 | var f func(n int) |
| 501 | f = func(n int) { |
| 502 | if n == 0 { |
| 503 | c <- true |
| 504 | return |
| 505 | } |
| 506 | go f(n - 1) |
| 507 | } |
| 508 | for i := 0; i < procs; i++ { |
| 509 | go f(b.N / procs) |
| 510 | } |
| 511 | for i := 0; i < procs; i++ { |
| 512 | <-c |
| 513 | } |
| 514 | } |
Dmitriy Vyukov | 72b09bd | 2013-03-01 00:41:45 +0200 | [diff] [blame] | 515 | |
Dmitry Vyukov | 0e80b2e | 2015-01-19 22:59:58 +0300 | [diff] [blame] | 516 | func BenchmarkCreateGoroutinesCapture(b *testing.B) { |
| 517 | b.ReportAllocs() |
| 518 | for i := 0; i < b.N; i++ { |
| 519 | const N = 4 |
| 520 | var wg sync.WaitGroup |
| 521 | wg.Add(N) |
| 522 | for i := 0; i < N; i++ { |
| 523 | i := i |
| 524 | go func() { |
| 525 | if i >= N { |
| 526 | b.Logf("bad") // just to capture b |
| 527 | } |
| 528 | wg.Done() |
| 529 | }() |
| 530 | } |
| 531 | wg.Wait() |
| 532 | } |
| 533 | } |
| 534 | |
Dmitry Vyukov | c4ee44b | 2015-02-06 15:09:46 +0300 | [diff] [blame] | 535 | func BenchmarkClosureCall(b *testing.B) { |
| 536 | sum := 0 |
| 537 | off1 := 1 |
| 538 | for i := 0; i < b.N; i++ { |
| 539 | off2 := 2 |
| 540 | func() { |
| 541 | sum += i + off1 + off2 |
| 542 | }() |
| 543 | } |
| 544 | _ = sum |
| 545 | } |
| 546 | |
Dmitriy Vyukov | 72b09bd | 2013-03-01 00:41:45 +0200 | [diff] [blame] | 547 | type Matrix [][]float64 |
| 548 | |
| 549 | func BenchmarkMatmult(b *testing.B) { |
| 550 | b.StopTimer() |
| 551 | // matmult is O(N**3) but testing expects O(b.N), |
| 552 | // so we need to take cube root of b.N |
| 553 | n := int(math.Cbrt(float64(b.N))) + 1 |
| 554 | A := makeMatrix(n) |
| 555 | B := makeMatrix(n) |
| 556 | C := makeMatrix(n) |
| 557 | b.StartTimer() |
| 558 | matmult(nil, A, B, C, 0, n, 0, n, 0, n, 8) |
| 559 | } |
| 560 | |
| 561 | func makeMatrix(n int) Matrix { |
| 562 | m := make(Matrix, n) |
| 563 | for i := 0; i < n; i++ { |
| 564 | m[i] = make([]float64, n) |
| 565 | for j := 0; j < n; j++ { |
| 566 | m[i][j] = float64(i*n + j) |
| 567 | } |
| 568 | } |
| 569 | return m |
| 570 | } |
| 571 | |
| 572 | func matmult(done chan<- struct{}, A, B, C Matrix, i0, i1, j0, j1, k0, k1, threshold int) { |
| 573 | di := i1 - i0 |
| 574 | dj := j1 - j0 |
| 575 | dk := k1 - k0 |
| 576 | if di >= dj && di >= dk && di >= threshold { |
| 577 | // divide in two by y axis |
| 578 | mi := i0 + di/2 |
| 579 | done1 := make(chan struct{}, 1) |
| 580 | go matmult(done1, A, B, C, i0, mi, j0, j1, k0, k1, threshold) |
| 581 | matmult(nil, A, B, C, mi, i1, j0, j1, k0, k1, threshold) |
| 582 | <-done1 |
| 583 | } else if dj >= dk && dj >= threshold { |
| 584 | // divide in two by x axis |
| 585 | mj := j0 + dj/2 |
| 586 | done1 := make(chan struct{}, 1) |
| 587 | go matmult(done1, A, B, C, i0, i1, j0, mj, k0, k1, threshold) |
| 588 | matmult(nil, A, B, C, i0, i1, mj, j1, k0, k1, threshold) |
| 589 | <-done1 |
| 590 | } else if dk >= threshold { |
| 591 | // divide in two by "k" axis |
| 592 | // deliberately not parallel because of data races |
| 593 | mk := k0 + dk/2 |
| 594 | matmult(nil, A, B, C, i0, i1, j0, j1, k0, mk, threshold) |
| 595 | matmult(nil, A, B, C, i0, i1, j0, j1, mk, k1, threshold) |
| 596 | } else { |
| 597 | // the matrices are small enough, compute directly |
| 598 | for i := i0; i < i1; i++ { |
| 599 | for j := j0; j < j1; j++ { |
| 600 | for k := k0; k < k1; k++ { |
| 601 | C[i][j] += A[i][k] * B[k][j] |
| 602 | } |
| 603 | } |
| 604 | } |
| 605 | } |
| 606 | if done != nil { |
| 607 | done <- struct{}{} |
| 608 | } |
| 609 | } |