1
0
mirror of https://github.com/golang/go synced 2024-11-15 11:30:36 -07:00
go/src/testing/benchmark_test.go
Ian Lance Taylor b0b1d42db3 all: change from sort functions to slices functions where feasible
Doing this because the slices functions are slightly faster and
slightly easier to use. It also removes one dependency layer.

This CL does not change packages that are used during bootstrap,
as the bootstrap compiler does not have the required slices functions.
It does not change the go/scanner package because the ErrorList
Len, Swap, and Less methods are part of the Go 1 API.

Change-Id: If52899be791c829198e11d2408727720b91ebe8a
Reviewed-on: https://go-review.googlesource.com/c/go/+/587655
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Commit-Queue: Ian Lance Taylor <iant@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
2024-05-23 01:00:11 +00:00

215 lines
5.6 KiB
Go

// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package testing_test
import (
"bytes"
"cmp"
"runtime"
"slices"
"strings"
"sync/atomic"
"testing"
"text/template"
"time"
)
var prettyPrintTests = []struct {
v float64
expected string
}{
{0, " 0 x"},
{1234.1, " 1234 x"},
{-1234.1, " -1234 x"},
{999.950001, " 1000 x"},
{999.949999, " 999.9 x"},
{99.9950001, " 100.0 x"},
{99.9949999, " 99.99 x"},
{-99.9949999, " -99.99 x"},
{0.000999950001, " 0.001000 x"},
{0.000999949999, " 0.0009999 x"}, // smallest case
{0.0000999949999, " 0.0001000 x"},
}
func TestPrettyPrint(t *testing.T) {
for _, tt := range prettyPrintTests {
buf := new(strings.Builder)
testing.PrettyPrint(buf, tt.v, "x")
if tt.expected != buf.String() {
t.Errorf("prettyPrint(%v): expected %q, actual %q", tt.v, tt.expected, buf.String())
}
}
}
func TestResultString(t *testing.T) {
// Test fractional ns/op handling
r := testing.BenchmarkResult{
N: 100,
T: 240 * time.Nanosecond,
}
if r.NsPerOp() != 2 {
t.Errorf("NsPerOp: expected 2, actual %v", r.NsPerOp())
}
if want, got := " 100\t 2.400 ns/op", r.String(); want != got {
t.Errorf("String: expected %q, actual %q", want, got)
}
// Test sub-1 ns/op (issue #31005)
r.T = 40 * time.Nanosecond
if want, got := " 100\t 0.4000 ns/op", r.String(); want != got {
t.Errorf("String: expected %q, actual %q", want, got)
}
// Test 0 ns/op
r.T = 0
if want, got := " 100", r.String(); want != got {
t.Errorf("String: expected %q, actual %q", want, got)
}
}
func TestRunParallel(t *testing.T) {
if testing.Short() {
t.Skip("skipping in short mode")
}
testing.Benchmark(func(b *testing.B) {
procs := uint32(0)
iters := uint64(0)
b.SetParallelism(3)
b.RunParallel(func(pb *testing.PB) {
atomic.AddUint32(&procs, 1)
for pb.Next() {
atomic.AddUint64(&iters, 1)
}
})
if want := uint32(3 * runtime.GOMAXPROCS(0)); procs != want {
t.Errorf("got %v procs, want %v", procs, want)
}
if iters != uint64(b.N) {
t.Errorf("got %v iters, want %v", iters, b.N)
}
})
}
func TestRunParallelFail(t *testing.T) {
testing.Benchmark(func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
// The function must be able to log/abort
// w/o crashing/deadlocking the whole benchmark.
b.Log("log")
b.Error("error")
})
})
}
func TestRunParallelFatal(t *testing.T) {
testing.Benchmark(func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if b.N > 1 {
b.Fatal("error")
}
}
})
})
}
func TestRunParallelSkipNow(t *testing.T) {
testing.Benchmark(func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if b.N > 1 {
b.SkipNow()
}
}
})
})
}
func ExampleB_RunParallel() {
// Parallel benchmark for text/template.Template.Execute on a single object.
testing.Benchmark(func(b *testing.B) {
templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
// RunParallel will create GOMAXPROCS goroutines
// and distribute work among them.
b.RunParallel(func(pb *testing.PB) {
// Each goroutine has its own bytes.Buffer.
var buf bytes.Buffer
for pb.Next() {
// The loop body is executed b.N times total across all goroutines.
buf.Reset()
templ.Execute(&buf, "World")
}
})
})
}
func TestReportMetric(t *testing.T) {
res := testing.Benchmark(func(b *testing.B) {
b.ReportMetric(12345, "ns/op")
b.ReportMetric(0.2, "frobs/op")
})
// Test built-in overriding.
if res.NsPerOp() != 12345 {
t.Errorf("NsPerOp: expected %v, actual %v", 12345, res.NsPerOp())
}
// Test stringing.
res.N = 1 // Make the output stable
want := " 1\t 12345 ns/op\t 0.2000 frobs/op"
if want != res.String() {
t.Errorf("expected %q, actual %q", want, res.String())
}
}
func ExampleB_ReportMetric() {
// This reports a custom benchmark metric relevant to a
// specific algorithm (in this case, sorting).
testing.Benchmark(func(b *testing.B) {
var compares int64
for i := 0; i < b.N; i++ {
s := []int{5, 4, 3, 2, 1}
slices.SortFunc(s, func(a, b int) int {
compares++
return cmp.Compare(a, b)
})
}
// This metric is per-operation, so divide by b.N and
// report it as a "/op" unit.
b.ReportMetric(float64(compares)/float64(b.N), "compares/op")
// This metric is per-time, so divide by b.Elapsed and
// report it as a "/ns" unit.
b.ReportMetric(float64(compares)/float64(b.Elapsed().Nanoseconds()), "compares/ns")
})
}
func ExampleB_ReportMetric_parallel() {
// This reports a custom benchmark metric relevant to a
// specific algorithm (in this case, sorting) in parallel.
testing.Benchmark(func(b *testing.B) {
var compares atomic.Int64
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
s := []int{5, 4, 3, 2, 1}
slices.SortFunc(s, func(a, b int) int {
// Because RunParallel runs the function many
// times in parallel, we must increment the
// counter atomically to avoid racing writes.
compares.Add(1)
return cmp.Compare(a, b)
})
}
})
// NOTE: Report each metric once, after all of the parallel
// calls have completed.
// This metric is per-operation, so divide by b.N and
// report it as a "/op" unit.
b.ReportMetric(float64(compares.Load())/float64(b.N), "compares/op")
// This metric is per-time, so divide by b.Elapsed and
// report it as a "/ns" unit.
b.ReportMetric(float64(compares.Load())/float64(b.Elapsed().Nanoseconds()), "compares/ns")
})
}