Black Lives Matter. Support the Equal Justice Initiative.

Source file src/testing/benchmark_test.go

Documentation: testing

     1  // Copyright 2013 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_test
     6  
     7  import (
     8  	"bytes"
     9  	"runtime"
    10  	"sort"
    11  	"strings"
    12  	"sync/atomic"
    13  	"testing"
    14  	"text/template"
    15  	"time"
    16  )
    17  
    18  var prettyPrintTests = []struct {
    19  	v        float64
    20  	expected string
    21  }{
    22  	{0, "         0 x"},
    23  	{1234.1, "      1234 x"},
    24  	{-1234.1, "     -1234 x"},
    25  	{999.950001, "      1000 x"},
    26  	{999.949999, "       999.9 x"},
    27  	{99.9950001, "       100.0 x"},
    28  	{99.9949999, "        99.99 x"},
    29  	{-99.9949999, "       -99.99 x"},
    30  	{0.000999950001, "         0.001000 x"},
    31  	{0.000999949999, "         0.0009999 x"}, // smallest case
    32  	{0.0000999949999, "         0.0001000 x"},
    33  }
    34  
    35  func TestPrettyPrint(t *testing.T) {
    36  	for _, tt := range prettyPrintTests {
    37  		buf := new(strings.Builder)
    38  		testing.PrettyPrint(buf, tt.v, "x")
    39  		if tt.expected != buf.String() {
    40  			t.Errorf("prettyPrint(%v): expected %q, actual %q", tt.v, tt.expected, buf.String())
    41  		}
    42  	}
    43  }
    44  
    45  func TestResultString(t *testing.T) {
    46  	// Test fractional ns/op handling
    47  	r := testing.BenchmarkResult{
    48  		N: 100,
    49  		T: 240 * time.Nanosecond,
    50  	}
    51  	if r.NsPerOp() != 2 {
    52  		t.Errorf("NsPerOp: expected 2, actual %v", r.NsPerOp())
    53  	}
    54  	if want, got := "     100\t         2.400 ns/op", r.String(); want != got {
    55  		t.Errorf("String: expected %q, actual %q", want, got)
    56  	}
    57  
    58  	// Test sub-1 ns/op (issue #31005)
    59  	r.T = 40 * time.Nanosecond
    60  	if want, got := "     100\t         0.4000 ns/op", r.String(); want != got {
    61  		t.Errorf("String: expected %q, actual %q", want, got)
    62  	}
    63  
    64  	// Test 0 ns/op
    65  	r.T = 0
    66  	if want, got := "     100", r.String(); want != got {
    67  		t.Errorf("String: expected %q, actual %q", want, got)
    68  	}
    69  }
    70  
    71  func TestRunParallel(t *testing.T) {
    72  	if testing.Short() {
    73  		t.Skip("skipping in short mode")
    74  	}
    75  	testing.Benchmark(func(b *testing.B) {
    76  		procs := uint32(0)
    77  		iters := uint64(0)
    78  		b.SetParallelism(3)
    79  		b.RunParallel(func(pb *testing.PB) {
    80  			atomic.AddUint32(&procs, 1)
    81  			for pb.Next() {
    82  				atomic.AddUint64(&iters, 1)
    83  			}
    84  		})
    85  		if want := uint32(3 * runtime.GOMAXPROCS(0)); procs != want {
    86  			t.Errorf("got %v procs, want %v", procs, want)
    87  		}
    88  		if iters != uint64(b.N) {
    89  			t.Errorf("got %v iters, want %v", iters, b.N)
    90  		}
    91  	})
    92  }
    93  
    94  func TestRunParallelFail(t *testing.T) {
    95  	testing.Benchmark(func(b *testing.B) {
    96  		b.RunParallel(func(pb *testing.PB) {
    97  			// The function must be able to log/abort
    98  			// w/o crashing/deadlocking the whole benchmark.
    99  			b.Log("log")
   100  			b.Error("error")
   101  		})
   102  	})
   103  }
   104  
   105  func TestRunParallelFatal(t *testing.T) {
   106  	testing.Benchmark(func(b *testing.B) {
   107  		b.RunParallel(func(pb *testing.PB) {
   108  			for pb.Next() {
   109  				if b.N > 1 {
   110  					b.Fatal("error")
   111  				}
   112  			}
   113  		})
   114  	})
   115  }
   116  
   117  func TestRunParallelSkipNow(t *testing.T) {
   118  	testing.Benchmark(func(b *testing.B) {
   119  		b.RunParallel(func(pb *testing.PB) {
   120  			for pb.Next() {
   121  				if b.N > 1 {
   122  					b.SkipNow()
   123  				}
   124  			}
   125  		})
   126  	})
   127  }
   128  
   129  func ExampleB_RunParallel() {
   130  	// Parallel benchmark for text/template.Template.Execute on a single object.
   131  	testing.Benchmark(func(b *testing.B) {
   132  		templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
   133  		// RunParallel will create GOMAXPROCS goroutines
   134  		// and distribute work among them.
   135  		b.RunParallel(func(pb *testing.PB) {
   136  			// Each goroutine has its own bytes.Buffer.
   137  			var buf bytes.Buffer
   138  			for pb.Next() {
   139  				// The loop body is executed b.N times total across all goroutines.
   140  				buf.Reset()
   141  				templ.Execute(&buf, "World")
   142  			}
   143  		})
   144  	})
   145  }
   146  
   147  func TestReportMetric(t *testing.T) {
   148  	res := testing.Benchmark(func(b *testing.B) {
   149  		b.ReportMetric(12345, "ns/op")
   150  		b.ReportMetric(0.2, "frobs/op")
   151  	})
   152  	// Test built-in overriding.
   153  	if res.NsPerOp() != 12345 {
   154  		t.Errorf("NsPerOp: expected %v, actual %v", 12345, res.NsPerOp())
   155  	}
   156  	// Test stringing.
   157  	res.N = 1 // Make the output stable
   158  	want := "       1\t     12345 ns/op\t         0.2000 frobs/op"
   159  	if want != res.String() {
   160  		t.Errorf("expected %q, actual %q", want, res.String())
   161  	}
   162  }
   163  
   164  func ExampleB_ReportMetric() {
   165  	// This reports a custom benchmark metric relevant to a
   166  	// specific algorithm (in this case, sorting).
   167  	testing.Benchmark(func(b *testing.B) {
   168  		var compares int64
   169  		for i := 0; i < b.N; i++ {
   170  			s := []int{5, 4, 3, 2, 1}
   171  			sort.Slice(s, func(i, j int) bool {
   172  				compares++
   173  				return s[i] < s[j]
   174  			})
   175  		}
   176  		// This metric is per-operation, so divide by b.N and
   177  		// report it as a "/op" unit.
   178  		b.ReportMetric(float64(compares)/float64(b.N), "compares/op")
   179  	})
   180  }
   181  

View as plain text