]> Cypherpunks.ru repositories - gostls13.git/blob - src/testing/benchmark.go
testing: fix fractional ns/op printing
[gostls13.git] / src / testing / benchmark.go
1 // Copyright 2009 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 testing
6
7 import (
8         "flag"
9         "fmt"
10         "internal/race"
11         "io"
12         "math"
13         "os"
14         "runtime"
15         "sort"
16         "strconv"
17         "strings"
18         "sync"
19         "sync/atomic"
20         "time"
21         "unicode"
22 )
23
24 var matchBenchmarks = flag.String("test.bench", "", "run only benchmarks matching `regexp`")
25 var benchTime = benchTimeFlag{d: 1 * time.Second}
26 var benchmarkMemory = flag.Bool("test.benchmem", false, "print memory allocations for benchmarks")
27
28 func init() {
29         flag.Var(&benchTime, "test.benchtime", "run each benchmark for duration `d`")
30 }
31
32 type benchTimeFlag struct {
33         d time.Duration
34         n int
35 }
36
37 func (f *benchTimeFlag) String() string {
38         if f.n > 0 {
39                 return fmt.Sprintf("%dx", f.n)
40         }
41         return time.Duration(f.d).String()
42 }
43
44 func (f *benchTimeFlag) Set(s string) error {
45         if strings.HasSuffix(s, "x") {
46                 n, err := strconv.ParseInt(s[:len(s)-1], 10, 0)
47                 if err != nil || n <= 0 {
48                         return fmt.Errorf("invalid count")
49                 }
50                 *f = benchTimeFlag{n: int(n)}
51                 return nil
52         }
53         d, err := time.ParseDuration(s)
54         if err != nil || d <= 0 {
55                 return fmt.Errorf("invalid duration")
56         }
57         *f = benchTimeFlag{d: d}
58         return nil
59 }
60
61 // Global lock to ensure only one benchmark runs at a time.
62 var benchmarkLock sync.Mutex
63
64 // Used for every benchmark for measuring memory.
65 var memStats runtime.MemStats
66
67 // An internal type but exported because it is cross-package; part of the implementation
68 // of the "go test" command.
69 type InternalBenchmark struct {
70         Name string
71         F    func(b *B)
72 }
73
74 // B is a type passed to Benchmark functions to manage benchmark
75 // timing and to specify the number of iterations to run.
76 //
77 // A benchmark ends when its Benchmark function returns or calls any of the methods
78 // FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods must be called
79 // only from the goroutine running the Benchmark function.
80 // The other reporting methods, such as the variations of Log and Error,
81 // may be called simultaneously from multiple goroutines.
82 //
83 // Like in tests, benchmark logs are accumulated during execution
84 // and dumped to standard error when done. Unlike in tests, benchmark logs
85 // are always printed, so as not to hide output whose existence may be
86 // affecting benchmark results.
87 type B struct {
88         common
89         importPath       string // import path of the package containing the benchmark
90         context          *benchContext
91         N                int
92         previousN        int           // number of iterations in the previous run
93         previousDuration time.Duration // total duration of the previous run
94         benchFunc        func(b *B)
95         benchTime        benchTimeFlag
96         bytes            int64
97         missingBytes     bool // one of the subbenchmarks does not have bytes set.
98         timerOn          bool
99         showAllocResult  bool
100         result           BenchmarkResult
101         parallelism      int // RunParallel creates parallelism*GOMAXPROCS goroutines
102         // The initial states of memStats.Mallocs and memStats.TotalAlloc.
103         startAllocs uint64
104         startBytes  uint64
105         // The net total of this test after being run.
106         netAllocs uint64
107         netBytes  uint64
108         // Extra metrics collected by ReportMetric.
109         extra map[string]float64
110 }
111
112 // StartTimer starts timing a test. This function is called automatically
113 // before a benchmark starts, but it can also be used to resume timing after
114 // a call to StopTimer.
115 func (b *B) StartTimer() {
116         if !b.timerOn {
117                 runtime.ReadMemStats(&memStats)
118                 b.startAllocs = memStats.Mallocs
119                 b.startBytes = memStats.TotalAlloc
120                 b.start = time.Now()
121                 b.timerOn = true
122         }
123 }
124
125 // StopTimer stops timing a test. This can be used to pause the timer
126 // while performing complex initialization that you don't
127 // want to measure.
128 func (b *B) StopTimer() {
129         if b.timerOn {
130                 b.duration += time.Since(b.start)
131                 runtime.ReadMemStats(&memStats)
132                 b.netAllocs += memStats.Mallocs - b.startAllocs
133                 b.netBytes += memStats.TotalAlloc - b.startBytes
134                 b.timerOn = false
135         }
136 }
137
138 // ResetTimer zeros the elapsed benchmark time and memory allocation counters
139 // and deletes user-reported metrics.
140 // It does not affect whether the timer is running.
141 func (b *B) ResetTimer() {
142         if b.extra == nil {
143                 // Allocate the extra map before reading memory stats.
144                 // Pre-size it to make more allocation unlikely.
145                 b.extra = make(map[string]float64, 16)
146         } else {
147                 for k := range b.extra {
148                         delete(b.extra, k)
149                 }
150         }
151         if b.timerOn {
152                 runtime.ReadMemStats(&memStats)
153                 b.startAllocs = memStats.Mallocs
154                 b.startBytes = memStats.TotalAlloc
155                 b.start = time.Now()
156         }
157         b.duration = 0
158         b.netAllocs = 0
159         b.netBytes = 0
160 }
161
162 // SetBytes records the number of bytes processed in a single operation.
163 // If this is called, the benchmark will report ns/op and MB/s.
164 func (b *B) SetBytes(n int64) { b.bytes = n }
165
166 // ReportAllocs enables malloc statistics for this benchmark.
167 // It is equivalent to setting -test.benchmem, but it only affects the
168 // benchmark function that calls ReportAllocs.
169 func (b *B) ReportAllocs() {
170         b.showAllocResult = true
171 }
172
173 // runN runs a single benchmark for the specified number of iterations.
174 func (b *B) runN(n int) {
175         benchmarkLock.Lock()
176         defer benchmarkLock.Unlock()
177         // Try to get a comparable environment for each run
178         // by clearing garbage from previous runs.
179         runtime.GC()
180         b.raceErrors = -race.Errors()
181         b.N = n
182         b.parallelism = 1
183         b.ResetTimer()
184         b.StartTimer()
185         b.benchFunc(b)
186         b.StopTimer()
187         b.previousN = n
188         b.previousDuration = b.duration
189         b.raceErrors += race.Errors()
190         if b.raceErrors > 0 {
191                 b.Errorf("race detected during execution of benchmark")
192         }
193 }
194
195 func min(x, y int64) int64 {
196         if x > y {
197                 return y
198         }
199         return x
200 }
201
202 func max(x, y int64) int64 {
203         if x < y {
204                 return y
205         }
206         return x
207 }
208
209 // run1 runs the first iteration of benchFunc. It reports whether more
210 // iterations of this benchmarks should be run.
211 func (b *B) run1() bool {
212         if ctx := b.context; ctx != nil {
213                 // Extend maxLen, if needed.
214                 if n := len(b.name) + ctx.extLen + 1; n > ctx.maxLen {
215                         ctx.maxLen = n + 8 // Add additional slack to avoid too many jumps in size.
216                 }
217         }
218         go func() {
219                 // Signal that we're done whether we return normally
220                 // or by FailNow's runtime.Goexit.
221                 defer func() {
222                         b.signal <- true
223                 }()
224
225                 b.runN(1)
226         }()
227         <-b.signal
228         if b.failed {
229                 fmt.Fprintf(b.w, "--- FAIL: %s\n%s", b.name, b.output)
230                 return false
231         }
232         // Only print the output if we know we are not going to proceed.
233         // Otherwise it is printed in processBench.
234         if atomic.LoadInt32(&b.hasSub) != 0 || b.finished {
235                 tag := "BENCH"
236                 if b.skipped {
237                         tag = "SKIP"
238                 }
239                 if b.chatty && (len(b.output) > 0 || b.finished) {
240                         b.trimOutput()
241                         fmt.Fprintf(b.w, "--- %s: %s\n%s", tag, b.name, b.output)
242                 }
243                 return false
244         }
245         return true
246 }
247
248 var labelsOnce sync.Once
249
250 // run executes the benchmark in a separate goroutine, including all of its
251 // subbenchmarks. b must not have subbenchmarks.
252 func (b *B) run() {
253         labelsOnce.Do(func() {
254                 fmt.Fprintf(b.w, "goos: %s\n", runtime.GOOS)
255                 fmt.Fprintf(b.w, "goarch: %s\n", runtime.GOARCH)
256                 if b.importPath != "" {
257                         fmt.Fprintf(b.w, "pkg: %s\n", b.importPath)
258                 }
259         })
260         if b.context != nil {
261                 // Running go test --test.bench
262                 b.context.processBench(b) // Must call doBench.
263         } else {
264                 // Running func Benchmark.
265                 b.doBench()
266         }
267 }
268
269 func (b *B) doBench() BenchmarkResult {
270         go b.launch()
271         <-b.signal
272         return b.result
273 }
274
275 // launch launches the benchmark function. It gradually increases the number
276 // of benchmark iterations until the benchmark runs for the requested benchtime.
277 // launch is run by the doBench function as a separate goroutine.
278 // run1 must have been called on b.
279 func (b *B) launch() {
280         // Signal that we're done whether we return normally
281         // or by FailNow's runtime.Goexit.
282         defer func() {
283                 b.signal <- true
284         }()
285
286         // Run the benchmark for at least the specified amount of time.
287         if b.benchTime.n > 0 {
288                 b.runN(b.benchTime.n)
289         } else {
290                 d := b.benchTime.d
291                 for n := int64(1); !b.failed && b.duration < d && n < 1e9; {
292                         last := n
293                         // Predict required iterations.
294                         goalns := d.Nanoseconds()
295                         prevIters := int64(b.N)
296                         prevns := b.duration.Nanoseconds()
297                         if prevns <= 0 {
298                                 // Round up, to avoid div by zero.
299                                 prevns = 1
300                         }
301                         // Order of operations matters.
302                         // For very fast benchmarks, prevIters ~= prevns.
303                         // If you divide first, you get 0 or 1,
304                         // which can hide an order of magnitude in execution time.
305                         // So multiply first, then divide.
306                         n = goalns * prevIters / prevns
307                         // Run more iterations than we think we'll need (1.2x).
308                         n += n / 5
309                         // Don't grow too fast in case we had timing errors previously.
310                         n = min(n, 100*last)
311                         // Be sure to run at least one more than last time.
312                         n = max(n, last+1)
313                         // Don't run more than 1e9 times. (This also keeps n in int range on 32 bit platforms.)
314                         n = min(n, 1e9)
315                         b.runN(int(n))
316                 }
317         }
318         b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes, b.extra}
319 }
320
321 // ReportMetric adds "n unit" to the reported benchmark results.
322 // If the metric is per-iteration, the caller should divide by b.N,
323 // and by convention units should end in "/op".
324 // ReportMetric overrides any previously reported value for the same unit.
325 // ReportMetric panics if unit is the empty string or if unit contains
326 // any whitespace.
327 // If unit is a unit normally reported by the benchmark framework itself
328 // (such as "allocs/op"), ReportMetric will override that metric.
329 // Setting "ns/op" to 0 will suppress that built-in metric.
330 func (b *B) ReportMetric(n float64, unit string) {
331         if unit == "" {
332                 panic("metric unit must not be empty")
333         }
334         if strings.IndexFunc(unit, unicode.IsSpace) >= 0 {
335                 panic("metric unit must not contain whitespace")
336         }
337         b.extra[unit] = n
338 }
339
340 // The results of a benchmark run.
341 type BenchmarkResult struct {
342         N         int           // The number of iterations.
343         T         time.Duration // The total time taken.
344         Bytes     int64         // Bytes processed in one iteration.
345         MemAllocs uint64        // The total number of memory allocations.
346         MemBytes  uint64        // The total number of bytes allocated.
347
348         // Extra records additional metrics reported by ReportMetric.
349         Extra map[string]float64
350 }
351
352 // NsPerOp returns the "ns/op" metric.
353 func (r BenchmarkResult) NsPerOp() int64 {
354         if v, ok := r.Extra["ns/op"]; ok {
355                 return int64(v)
356         }
357         if r.N <= 0 {
358                 return 0
359         }
360         return r.T.Nanoseconds() / int64(r.N)
361 }
362
363 // mbPerSec returns the "MB/s" metric.
364 func (r BenchmarkResult) mbPerSec() float64 {
365         if v, ok := r.Extra["MB/s"]; ok {
366                 return v
367         }
368         if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 {
369                 return 0
370         }
371         return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds()
372 }
373
374 // AllocsPerOp returns the "allocs/op" metric,
375 // which is calculated as r.MemAllocs / r.N.
376 func (r BenchmarkResult) AllocsPerOp() int64 {
377         if v, ok := r.Extra["allocs/op"]; ok {
378                 return int64(v)
379         }
380         if r.N <= 0 {
381                 return 0
382         }
383         return int64(r.MemAllocs) / int64(r.N)
384 }
385
386 // AllocedBytesPerOp returns the "B/op" metric,
387 // which is calculated as r.MemBytes / r.N.
388 func (r BenchmarkResult) AllocedBytesPerOp() int64 {
389         if v, ok := r.Extra["B/op"]; ok {
390                 return int64(v)
391         }
392         if r.N <= 0 {
393                 return 0
394         }
395         return int64(r.MemBytes) / int64(r.N)
396 }
397
398 // String returns a summary of the benchmark results.
399 // It follows the benchmark result line format from
400 // https://golang.org/design/14313-benchmark-format, not including the
401 // benchmark name.
402 // Extra metrics override built-in metrics of the same name.
403 // String does not include allocs/op or B/op, since those are reported
404 // by MemString.
405 func (r BenchmarkResult) String() string {
406         buf := new(strings.Builder)
407         fmt.Fprintf(buf, "%8d", r.N)
408
409         // Get ns/op as a float.
410         ns, ok := r.Extra["ns/op"]
411         if !ok {
412                 ns = float64(r.T.Nanoseconds()) / float64(r.N)
413         }
414         if ns != 0 {
415                 buf.WriteByte('\t')
416                 prettyPrint(buf, ns, "ns/op")
417         }
418
419         if mbs := r.mbPerSec(); mbs != 0 {
420                 fmt.Fprintf(buf, "\t%7.2f MB/s", mbs)
421         }
422
423         // Print extra metrics that aren't represented in the standard
424         // metrics.
425         var extraKeys []string
426         for k := range r.Extra {
427                 switch k {
428                 case "ns/op", "MB/s", "B/op", "allocs/op":
429                         // Built-in metrics reported elsewhere.
430                         continue
431                 }
432                 extraKeys = append(extraKeys, k)
433         }
434         sort.Strings(extraKeys)
435         for _, k := range extraKeys {
436                 buf.WriteByte('\t')
437                 prettyPrint(buf, r.Extra[k], k)
438         }
439         return buf.String()
440 }
441
442 func prettyPrint(w io.Writer, x float64, unit string) {
443         // Print all numbers with 10 places before the decimal point
444         // and small numbers with three sig figs.
445         var format string
446         switch y := math.Abs(x); {
447         case y == 0 || y >= 99.95:
448                 format = "%10.0f %s"
449         case y >= 9.995:
450                 format = "%12.1f %s"
451         case y >= 0.9995:
452                 format = "%13.2f %s"
453         case y >= 0.09995:
454                 format = "%14.3f %s"
455         case y >= 0.009995:
456                 format = "%15.4f %s"
457         case y >= 0.0009995:
458                 format = "%16.5f %s"
459         default:
460                 format = "%17.6f %s"
461         }
462         fmt.Fprintf(w, format, x, unit)
463 }
464
465 // MemString returns r.AllocedBytesPerOp and r.AllocsPerOp in the same format as 'go test'.
466 func (r BenchmarkResult) MemString() string {
467         return fmt.Sprintf("%8d B/op\t%8d allocs/op",
468                 r.AllocedBytesPerOp(), r.AllocsPerOp())
469 }
470
471 // benchmarkName returns full name of benchmark including procs suffix.
472 func benchmarkName(name string, n int) string {
473         if n != 1 {
474                 return fmt.Sprintf("%s-%d", name, n)
475         }
476         return name
477 }
478
479 type benchContext struct {
480         match *matcher
481
482         maxLen int // The largest recorded benchmark name.
483         extLen int // Maximum extension length.
484 }
485
486 // An internal function but exported because it is cross-package; part of the implementation
487 // of the "go test" command.
488 func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
489         runBenchmarks("", matchString, benchmarks)
490 }
491
492 func runBenchmarks(importPath string, matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) bool {
493         // If no flag was specified, don't run benchmarks.
494         if len(*matchBenchmarks) == 0 {
495                 return true
496         }
497         // Collect matching benchmarks and determine longest name.
498         maxprocs := 1
499         for _, procs := range cpuList {
500                 if procs > maxprocs {
501                         maxprocs = procs
502                 }
503         }
504         ctx := &benchContext{
505                 match:  newMatcher(matchString, *matchBenchmarks, "-test.bench"),
506                 extLen: len(benchmarkName("", maxprocs)),
507         }
508         var bs []InternalBenchmark
509         for _, Benchmark := range benchmarks {
510                 if _, matched, _ := ctx.match.fullName(nil, Benchmark.Name); matched {
511                         bs = append(bs, Benchmark)
512                         benchName := benchmarkName(Benchmark.Name, maxprocs)
513                         if l := len(benchName) + ctx.extLen + 1; l > ctx.maxLen {
514                                 ctx.maxLen = l
515                         }
516                 }
517         }
518         main := &B{
519                 common: common{
520                         name:   "Main",
521                         w:      os.Stdout,
522                         chatty: *chatty,
523                 },
524                 importPath: importPath,
525                 benchFunc: func(b *B) {
526                         for _, Benchmark := range bs {
527                                 b.Run(Benchmark.Name, Benchmark.F)
528                         }
529                 },
530                 benchTime: benchTime,
531                 context:   ctx,
532         }
533         main.runN(1)
534         return !main.failed
535 }
536
537 // processBench runs bench b for the configured CPU counts and prints the results.
538 func (ctx *benchContext) processBench(b *B) {
539         for i, procs := range cpuList {
540                 for j := uint(0); j < *count; j++ {
541                         runtime.GOMAXPROCS(procs)
542                         benchName := benchmarkName(b.name, procs)
543                         fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName)
544                         // Recompute the running time for all but the first iteration.
545                         if i > 0 || j > 0 {
546                                 b = &B{
547                                         common: common{
548                                                 signal: make(chan bool),
549                                                 name:   b.name,
550                                                 w:      b.w,
551                                                 chatty: b.chatty,
552                                         },
553                                         benchFunc: b.benchFunc,
554                                         benchTime: b.benchTime,
555                                 }
556                                 b.run1()
557                         }
558                         r := b.doBench()
559                         if b.failed {
560                                 // The output could be very long here, but probably isn't.
561                                 // We print it all, regardless, because we don't want to trim the reason
562                                 // the benchmark failed.
563                                 fmt.Fprintf(b.w, "--- FAIL: %s\n%s", benchName, b.output)
564                                 continue
565                         }
566                         results := r.String()
567                         if *benchmarkMemory || b.showAllocResult {
568                                 results += "\t" + r.MemString()
569                         }
570                         fmt.Fprintln(b.w, results)
571                         // Unlike with tests, we ignore the -chatty flag and always print output for
572                         // benchmarks since the output generation time will skew the results.
573                         if len(b.output) > 0 {
574                                 b.trimOutput()
575                                 fmt.Fprintf(b.w, "--- BENCH: %s\n%s", benchName, b.output)
576                         }
577                         if p := runtime.GOMAXPROCS(-1); p != procs {
578                                 fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
579                         }
580                 }
581         }
582 }
583
584 // Run benchmarks f as a subbenchmark with the given name. It reports
585 // whether there were any failures.
586 //
587 // A subbenchmark is like any other benchmark. A benchmark that calls Run at
588 // least once will not be measured itself and will be called once with N=1.
589 func (b *B) Run(name string, f func(b *B)) bool {
590         // Since b has subbenchmarks, we will no longer run it as a benchmark itself.
591         // Release the lock and acquire it on exit to ensure locks stay paired.
592         atomic.StoreInt32(&b.hasSub, 1)
593         benchmarkLock.Unlock()
594         defer benchmarkLock.Lock()
595
596         benchName, ok, partial := b.name, true, false
597         if b.context != nil {
598                 benchName, ok, partial = b.context.match.fullName(&b.common, name)
599         }
600         if !ok {
601                 return true
602         }
603         var pc [maxStackLen]uintptr
604         n := runtime.Callers(2, pc[:])
605         sub := &B{
606                 common: common{
607                         signal:  make(chan bool),
608                         name:    benchName,
609                         parent:  &b.common,
610                         level:   b.level + 1,
611                         creator: pc[:n],
612                         w:       b.w,
613                         chatty:  b.chatty,
614                 },
615                 importPath: b.importPath,
616                 benchFunc:  f,
617                 benchTime:  b.benchTime,
618                 context:    b.context,
619         }
620         if partial {
621                 // Partial name match, like -bench=X/Y matching BenchmarkX.
622                 // Only process sub-benchmarks, if any.
623                 atomic.StoreInt32(&sub.hasSub, 1)
624         }
625         if sub.run1() {
626                 sub.run()
627         }
628         b.add(sub.result)
629         return !sub.failed
630 }
631
632 // add simulates running benchmarks in sequence in a single iteration. It is
633 // used to give some meaningful results in case func Benchmark is used in
634 // combination with Run.
635 func (b *B) add(other BenchmarkResult) {
636         r := &b.result
637         // The aggregated BenchmarkResults resemble running all subbenchmarks as
638         // in sequence in a single benchmark.
639         r.N = 1
640         r.T += time.Duration(other.NsPerOp())
641         if other.Bytes == 0 {
642                 // Summing Bytes is meaningless in aggregate if not all subbenchmarks
643                 // set it.
644                 b.missingBytes = true
645                 r.Bytes = 0
646         }
647         if !b.missingBytes {
648                 r.Bytes += other.Bytes
649         }
650         r.MemAllocs += uint64(other.AllocsPerOp())
651         r.MemBytes += uint64(other.AllocedBytesPerOp())
652 }
653
654 // trimOutput shortens the output from a benchmark, which can be very long.
655 func (b *B) trimOutput() {
656         // The output is likely to appear multiple times because the benchmark
657         // is run multiple times, but at least it will be seen. This is not a big deal
658         // because benchmarks rarely print, but just in case, we trim it if it's too long.
659         const maxNewlines = 10
660         for nlCount, j := 0, 0; j < len(b.output); j++ {
661                 if b.output[j] == '\n' {
662                         nlCount++
663                         if nlCount >= maxNewlines {
664                                 b.output = append(b.output[:j], "\n\t... [output truncated]\n"...)
665                                 break
666                         }
667                 }
668         }
669 }
670
671 // A PB is used by RunParallel for running parallel benchmarks.
672 type PB struct {
673         globalN *uint64 // shared between all worker goroutines iteration counter
674         grain   uint64  // acquire that many iterations from globalN at once
675         cache   uint64  // local cache of acquired iterations
676         bN      uint64  // total number of iterations to execute (b.N)
677 }
678
679 // Next reports whether there are more iterations to execute.
680 func (pb *PB) Next() bool {
681         if pb.cache == 0 {
682                 n := atomic.AddUint64(pb.globalN, pb.grain)
683                 if n <= pb.bN {
684                         pb.cache = pb.grain
685                 } else if n < pb.bN+pb.grain {
686                         pb.cache = pb.bN + pb.grain - n
687                 } else {
688                         return false
689                 }
690         }
691         pb.cache--
692         return true
693 }
694
695 // RunParallel runs a benchmark in parallel.
696 // It creates multiple goroutines and distributes b.N iterations among them.
697 // The number of goroutines defaults to GOMAXPROCS. To increase parallelism for
698 // non-CPU-bound benchmarks, call SetParallelism before RunParallel.
699 // RunParallel is usually used with the go test -cpu flag.
700 //
701 // The body function will be run in each goroutine. It should set up any
702 // goroutine-local state and then iterate until pb.Next returns false.
703 // It should not use the StartTimer, StopTimer, or ResetTimer functions,
704 // because they have global effect. It should also not call Run.
705 func (b *B) RunParallel(body func(*PB)) {
706         if b.N == 0 {
707                 return // Nothing to do when probing.
708         }
709         // Calculate grain size as number of iterations that take ~100µs.
710         // 100µs is enough to amortize the overhead and provide sufficient
711         // dynamic load balancing.
712         grain := uint64(0)
713         if b.previousN > 0 && b.previousDuration > 0 {
714                 grain = 1e5 * uint64(b.previousN) / uint64(b.previousDuration)
715         }
716         if grain < 1 {
717                 grain = 1
718         }
719         // We expect the inner loop and function call to take at least 10ns,
720         // so do not do more than 100µs/10ns=1e4 iterations.
721         if grain > 1e4 {
722                 grain = 1e4
723         }
724
725         n := uint64(0)
726         numProcs := b.parallelism * runtime.GOMAXPROCS(0)
727         var wg sync.WaitGroup
728         wg.Add(numProcs)
729         for p := 0; p < numProcs; p++ {
730                 go func() {
731                         defer wg.Done()
732                         pb := &PB{
733                                 globalN: &n,
734                                 grain:   grain,
735                                 bN:      uint64(b.N),
736                         }
737                         body(pb)
738                 }()
739         }
740         wg.Wait()
741         if n <= uint64(b.N) && !b.Failed() {
742                 b.Fatal("RunParallel: body exited without pb.Next() == false")
743         }
744 }
745
746 // SetParallelism sets the number of goroutines used by RunParallel to p*GOMAXPROCS.
747 // There is usually no need to call SetParallelism for CPU-bound benchmarks.
748 // If p is less than 1, this call will have no effect.
749 func (b *B) SetParallelism(p int) {
750         if p >= 1 {
751                 b.parallelism = p
752         }
753 }
754
755 // Benchmark benchmarks a single function. Useful for creating
756 // custom benchmarks that do not use the "go test" command.
757 //
758 // If f calls Run, the result will be an estimate of running all its
759 // subbenchmarks that don't call Run in sequence in a single benchmark.
760 func Benchmark(f func(b *B)) BenchmarkResult {
761         b := &B{
762                 common: common{
763                         signal: make(chan bool),
764                         w:      discard{},
765                 },
766                 benchFunc: f,
767                 benchTime: benchTime,
768         }
769         if b.run1() {
770                 b.run()
771         }
772         return b.result
773 }
774
775 type discard struct{}
776
777 func (discard) Write(b []byte) (n int, err error) { return len(b), nil }