OLD | NEW |
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package testing | 5 package testing |
6 | 6 |
7 import ( | 7 import ( |
8 "flag" | 8 "flag" |
9 "fmt" | 9 "fmt" |
10 "os" | 10 "os" |
(...skipping 25 matching lines...) Expand all Loading... |
36 type B struct { | 36 type B struct { |
37 common | 37 common |
38 N int | 38 N int |
39 previousN int // number of iterations in the previous r
un | 39 previousN int // number of iterations in the previous r
un |
40 previousDuration time.Duration // total duration of the previous run | 40 previousDuration time.Duration // total duration of the previous run |
41 benchmark InternalBenchmark | 41 benchmark InternalBenchmark |
42 bytes int64 | 42 bytes int64 |
43 timerOn bool | 43 timerOn bool |
44 showAllocResult bool | 44 showAllocResult bool |
45 result BenchmarkResult | 45 result BenchmarkResult |
46 » parallelism int // RunParallel creates parallelism*GOMAXPROCS gorou
tines | 46 » concurrency int // RunConcurrent creates concurrency*GOMAXPROCS gor
outines |
47 // The initial states of memStats.Mallocs and memStats.TotalAlloc. | 47 // The initial states of memStats.Mallocs and memStats.TotalAlloc. |
48 startAllocs uint64 | 48 startAllocs uint64 |
49 startBytes uint64 | 49 startBytes uint64 |
50 // The net total of this test after being run. | 50 // The net total of this test after being run. |
51 netAllocs uint64 | 51 netAllocs uint64 |
52 netBytes uint64 | 52 netBytes uint64 |
53 } | 53 } |
54 | 54 |
55 // StartTimer starts timing a test. This function is called automatically | 55 // StartTimer starts timing a test. This function is called automatically |
56 // before a benchmark starts, but it can also used to resume timing after | 56 // before a benchmark starts, but it can also used to resume timing after |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 } | 111 } |
112 | 112 |
113 // runN runs a single benchmark for the specified number of iterations. | 113 // runN runs a single benchmark for the specified number of iterations. |
114 func (b *B) runN(n int) { | 114 func (b *B) runN(n int) { |
115 benchmarkLock.Lock() | 115 benchmarkLock.Lock() |
116 defer benchmarkLock.Unlock() | 116 defer benchmarkLock.Unlock() |
117 // Try to get a comparable environment for each run | 117 // Try to get a comparable environment for each run |
118 // by clearing garbage from previous runs. | 118 // by clearing garbage from previous runs. |
119 runtime.GC() | 119 runtime.GC() |
120 b.N = n | 120 b.N = n |
121 » b.parallelism = 1 | 121 » b.concurrency = 1 |
122 b.ResetTimer() | 122 b.ResetTimer() |
123 b.StartTimer() | 123 b.StartTimer() |
124 b.benchmark.F(b) | 124 b.benchmark.F(b) |
125 b.StopTimer() | 125 b.StopTimer() |
126 b.previousN = n | 126 b.previousN = n |
127 b.previousDuration = b.duration | 127 b.previousDuration = b.duration |
128 } | 128 } |
129 | 129 |
130 func min(x, y int) int { | 130 func min(x, y int) int { |
131 if x > y { | 131 if x > y { |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 if b.output[j] == '\n' { | 343 if b.output[j] == '\n' { |
344 nlCount++ | 344 nlCount++ |
345 if nlCount >= maxNewlines { | 345 if nlCount >= maxNewlines { |
346 b.output = append(b.output[:j], "\n\t... [output
truncated]\n"...) | 346 b.output = append(b.output[:j], "\n\t... [output
truncated]\n"...) |
347 break | 347 break |
348 } | 348 } |
349 } | 349 } |
350 } | 350 } |
351 } | 351 } |
352 | 352 |
353 // A PB is used by RunParallel for running parallel benchmarks. | 353 // A PB is used by RunConcurrent for running parallel benchmarks. |
354 type PB struct { | 354 type PB struct { |
355 globalN *uint64 // shared between all worker goroutines iteration counte
r | 355 globalN *uint64 // shared between all worker goroutines iteration counte
r |
356 grain uint64 // acquire that many iterations from globalN at once | 356 grain uint64 // acquire that many iterations from globalN at once |
357 cache uint64 // local cache of acquired iterations | 357 cache uint64 // local cache of acquired iterations |
358 bN uint64 // total number of iterations to execute (b.N) | 358 bN uint64 // total number of iterations to execute (b.N) |
359 } | 359 } |
360 | 360 |
361 // Next reports whether there are more iterations to execute. | 361 // Next reports whether there are more iterations to execute. |
362 func (pb *PB) Next() bool { | 362 func (pb *PB) Next() bool { |
363 if pb.cache == 0 { | 363 if pb.cache == 0 { |
364 n := atomic.AddUint64(pb.globalN, pb.grain) | 364 n := atomic.AddUint64(pb.globalN, pb.grain) |
365 if n <= pb.bN { | 365 if n <= pb.bN { |
366 pb.cache = pb.grain | 366 pb.cache = pb.grain |
367 } else if n < pb.bN+pb.grain { | 367 } else if n < pb.bN+pb.grain { |
368 pb.cache = pb.bN + pb.grain - n | 368 pb.cache = pb.bN + pb.grain - n |
369 } else { | 369 } else { |
370 return false | 370 return false |
371 } | 371 } |
372 } | 372 } |
373 pb.cache-- | 373 pb.cache-- |
374 return true | 374 return true |
375 } | 375 } |
376 | 376 |
377 // RunParallel runs a benchmark in parallel. | 377 // RunConcurrent distributes benchmark runs among a set of goroutines. |
378 // It creates multiple goroutines and distributes b.N iterations among them. | 378 // RunConcurrent measures how a benchmark scales with increasing concurrecy. It |
379 // The number of goroutines defaults to GOMAXPROCS. To increase parallelism for | 379 // is usually used with the go test -cpu flag to measure scaling with increasing |
380 // non-CPU-bound benchmarks, call SetParallelism before RunParallel. | 380 // parallelism. |
381 // RunParallel is usually used with the go test -cpu flag. | 381 // |
| 382 // By default, RunConcurrent creates GOMAXPROCS goroutines and distributes b.N |
| 383 // iterations among them. The number of goroutines can be changed by calling |
| 384 // SetConcurrency before running RunConcurrent. |
382 // | 385 // |
383 // The body function will be run in each goroutine. It should set up any | 386 // The body function will be run in each goroutine. It should set up any |
384 // goroutine-local state and then iterate until pb.Next returns false. | 387 // goroutine-local state and then iterate until pb.Next returns false. |
385 // It should not use the StartTimer, StopTimer, or ResetTimer functions, | 388 // It should not use the StartTimer, StopTimer, or ResetTimer functions, |
386 // because they have global effect. | 389 // because they have global effect. |
387 func (b *B) RunParallel(body func(*PB)) { | 390 func (b *B) RunConcurrent(body func(*PB)) { |
388 // Calculate grain size as number of iterations that take ~100µs. | 391 // Calculate grain size as number of iterations that take ~100µs. |
389 // 100µs is enough to amortize the overhead and provide sufficient | 392 // 100µs is enough to amortize the overhead and provide sufficient |
390 // dynamic load balancing. | 393 // dynamic load balancing. |
391 grain := uint64(0) | 394 grain := uint64(0) |
392 if b.previousN > 0 && b.previousDuration > 0 { | 395 if b.previousN > 0 && b.previousDuration > 0 { |
393 grain = 1e5 * uint64(b.previousN) / uint64(b.previousDuration) | 396 grain = 1e5 * uint64(b.previousN) / uint64(b.previousDuration) |
394 } | 397 } |
395 if grain < 1 { | 398 if grain < 1 { |
396 grain = 1 | 399 grain = 1 |
397 } | 400 } |
398 // We expect the inner loop and function call to take at least 10ns, | 401 // We expect the inner loop and function call to take at least 10ns, |
399 // so do not do more than 100µs/10ns=1e4 iterations. | 402 // so do not do more than 100µs/10ns=1e4 iterations. |
400 if grain > 1e4 { | 403 if grain > 1e4 { |
401 grain = 1e4 | 404 grain = 1e4 |
402 } | 405 } |
403 | 406 |
404 n := uint64(0) | 407 n := uint64(0) |
405 » numProcs := b.parallelism * runtime.GOMAXPROCS(0) | 408 » numProcs := b.concurrency * runtime.GOMAXPROCS(0) |
406 var wg sync.WaitGroup | 409 var wg sync.WaitGroup |
407 wg.Add(numProcs) | 410 wg.Add(numProcs) |
408 for p := 0; p < numProcs; p++ { | 411 for p := 0; p < numProcs; p++ { |
409 go func() { | 412 go func() { |
410 defer wg.Done() | 413 defer wg.Done() |
411 pb := &PB{ | 414 pb := &PB{ |
412 globalN: &n, | 415 globalN: &n, |
413 grain: grain, | 416 grain: grain, |
414 bN: uint64(b.N), | 417 bN: uint64(b.N), |
415 } | 418 } |
416 body(pb) | 419 body(pb) |
417 }() | 420 }() |
418 } | 421 } |
419 wg.Wait() | 422 wg.Wait() |
420 if n <= uint64(b.N) && !b.Failed() { | 423 if n <= uint64(b.N) && !b.Failed() { |
421 » » b.Fatal("RunParallel: body exited without pb.Next() == false") | 424 » » b.Fatal("RunConcurrent: body exited without pb.Next() == false") |
422 } | 425 } |
423 } | 426 } |
424 | 427 |
425 // SetParallelism sets the number of goroutines used by RunParallel to p*GOMAXPR
OCS. | 428 // SetConcurrency sets the number of goroutines used by RunConcurrent to |
426 // There is usually no need to call SetParallelism for CPU-bound benchmarks. | 429 // p*GOMAXPROCS. There is usually no need to call SetConcurrency for CPU-bound |
427 // If p is less than 1, this call will have no effect. | 430 // benchmarks. If p is less than 1, this call will have no effect. |
428 func (b *B) SetParallelism(p int) { | 431 func (b *B) SetConcurrency(p int) { |
429 if p >= 1 { | 432 if p >= 1 { |
430 » » b.parallelism = p | 433 » » b.concurrency = p |
431 } | 434 } |
432 } | 435 } |
433 | 436 |
434 // Benchmark benchmarks a single function. Useful for creating | 437 // Benchmark benchmarks a single function. Useful for creating |
435 // custom benchmarks that do not use the "go test" command. | 438 // custom benchmarks that do not use the "go test" command. |
436 func Benchmark(f func(b *B)) BenchmarkResult { | 439 func Benchmark(f func(b *B)) BenchmarkResult { |
437 b := &B{ | 440 b := &B{ |
438 common: common{ | 441 common: common{ |
439 signal: make(chan interface{}), | 442 signal: make(chan interface{}), |
440 }, | 443 }, |
441 benchmark: InternalBenchmark{"", f}, | 444 benchmark: InternalBenchmark{"", f}, |
442 } | 445 } |
443 return b.run() | 446 return b.run() |
444 } | 447 } |
OLD | NEW |