mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
testing: add AllocsPerRun
This CL also replaces similar loops in other stdlib package tests with calls to AllocsPerRun. Fixes #4461. R=minux.ma, rsc CC=golang-dev https://golang.org/cl/7002055
This commit is contained in:
parent
f418c505d0
commit
9bfd3c3937
10 changed files with 105 additions and 144 deletions
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -50,49 +49,43 @@ func BenchmarkEndToEndByteBuffer(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCountEncodeMallocs(t *testing.T) {
|
func TestCountEncodeMallocs(t *testing.T) {
|
||||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
const N = 1000
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
enc := NewEncoder(&buf)
|
enc := NewEncoder(&buf)
|
||||||
bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
|
bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
|
||||||
memstats := new(runtime.MemStats)
|
|
||||||
runtime.ReadMemStats(memstats)
|
allocs := testing.AllocsPerRun(N, func() {
|
||||||
mallocs := 0 - memstats.Mallocs
|
|
||||||
const count = 1000
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
err := enc.Encode(bench)
|
err := enc.Encode(bench)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("encode:", err)
|
t.Fatal("encode:", err)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
runtime.ReadMemStats(memstats)
|
fmt.Printf("mallocs per encode of type Bench: %v\n", allocs)
|
||||||
mallocs += memstats.Mallocs
|
|
||||||
fmt.Printf("mallocs per encode of type Bench: %d\n", mallocs/count)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCountDecodeMallocs(t *testing.T) {
|
func TestCountDecodeMallocs(t *testing.T) {
|
||||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
const N = 1000
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
enc := NewEncoder(&buf)
|
enc := NewEncoder(&buf)
|
||||||
bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
|
bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
|
||||||
const count = 1000
|
|
||||||
for i := 0; i < count; i++ {
|
// Fill the buffer with enough to decode
|
||||||
|
testing.AllocsPerRun(N, func() {
|
||||||
err := enc.Encode(bench)
|
err := enc.Encode(bench)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("encode:", err)
|
t.Fatal("encode:", err)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
dec := NewDecoder(&buf)
|
dec := NewDecoder(&buf)
|
||||||
memstats := new(runtime.MemStats)
|
allocs := testing.AllocsPerRun(N, func() {
|
||||||
runtime.ReadMemStats(memstats)
|
|
||||||
mallocs := 0 - memstats.Mallocs
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
*bench = Bench{}
|
*bench = Bench{}
|
||||||
err := dec.Decode(&bench)
|
err := dec.Decode(&bench)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("decode:", err)
|
t.Fatal("decode:", err)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
runtime.ReadMemStats(memstats)
|
fmt.Printf("mallocs per decode of type Bench: %v\n", allocs)
|
||||||
mallocs += memstats.Mallocs
|
|
||||||
fmt.Printf("mallocs per decode of type Bench: %d\n", mallocs/count)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
. "fmt"
|
. "fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"runtime" // for the malloc count test only
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -598,19 +597,10 @@ var mallocTest = []struct {
|
||||||
var _ bytes.Buffer
|
var _ bytes.Buffer
|
||||||
|
|
||||||
func TestCountMallocs(t *testing.T) {
|
func TestCountMallocs(t *testing.T) {
|
||||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
|
||||||
for _, mt := range mallocTest {
|
for _, mt := range mallocTest {
|
||||||
const N = 100
|
mallocs := testing.AllocsPerRun(100, mt.fn)
|
||||||
memstats := new(runtime.MemStats)
|
if got, max := mallocs, float64(mt.count); got > max {
|
||||||
runtime.ReadMemStats(memstats)
|
t.Errorf("%s: got %v allocs, want <=%v", mt.desc, got, max)
|
||||||
mallocs := 0 - memstats.Mallocs
|
|
||||||
for i := 0; i < N; i++ {
|
|
||||||
mt.fn()
|
|
||||||
}
|
|
||||||
runtime.ReadMemStats(memstats)
|
|
||||||
mallocs += memstats.Mallocs
|
|
||||||
if mallocs/N > uint64(mt.count) {
|
|
||||||
t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -175,38 +174,31 @@ func TestHasToken(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testHeader = Header{
|
||||||
|
"Content-Length": {"123"},
|
||||||
|
"Content-Type": {"text/plain"},
|
||||||
|
"Date": {"some date at some time Z"},
|
||||||
|
"Server": {"Go http package"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
func BenchmarkHeaderWriteSubset(b *testing.B) {
|
func BenchmarkHeaderWriteSubset(b *testing.B) {
|
||||||
doHeaderWriteSubset(b.N, b)
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
buf.Reset()
|
||||||
|
testHeader.WriteSubset(&buf, nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeaderWriteSubsetMallocs(t *testing.T) {
|
func TestHeaderWriteSubsetMallocs(t *testing.T) {
|
||||||
doHeaderWriteSubset(100, t)
|
n := testing.AllocsPerRun(100, func() {
|
||||||
}
|
|
||||||
|
|
||||||
type errorfer interface {
|
|
||||||
Errorf(string, ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func doHeaderWriteSubset(n int, t errorfer) {
|
|
||||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
|
||||||
h := Header(map[string][]string{
|
|
||||||
"Content-Length": {"123"},
|
|
||||||
"Content-Type": {"text/plain"},
|
|
||||||
"Date": {"some date at some time Z"},
|
|
||||||
"Server": {"Go http package"},
|
|
||||||
})
|
|
||||||
var buf bytes.Buffer
|
|
||||||
var m0 runtime.MemStats
|
|
||||||
runtime.ReadMemStats(&m0)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
h.WriteSubset(&buf, nil)
|
testHeader.WriteSubset(&buf, nil)
|
||||||
}
|
})
|
||||||
var m1 runtime.MemStats
|
if n > 1 {
|
||||||
runtime.ReadMemStats(&m1)
|
|
||||||
if mallocs := m1.Mallocs - m0.Mallocs; n >= 100 && mallocs >= uint64(n) {
|
|
||||||
// TODO(bradfitz,rsc): once we can sort without allocating,
|
// TODO(bradfitz,rsc): once we can sort without allocating,
|
||||||
// make this an error. See http://golang.org/issue/3761
|
// make this an error. See http://golang.org/issue/3761
|
||||||
// t.Errorf("did %d mallocs (>= %d iterations); should have avoided mallocs", mallocs, n)
|
// t.Errorf("got %v allocs, want <= %v", n, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -445,8 +445,7 @@ func dialHTTP() (*Client, error) {
|
||||||
return DialHTTP("tcp", httpServerAddr)
|
return DialHTTP("tcp", httpServerAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func countMallocs(dial func() (*Client, error), t *testing.T) uint64 {
|
func countMallocs(dial func() (*Client, error), t *testing.T) float64 {
|
||||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
|
||||||
once.Do(startServer)
|
once.Do(startServer)
|
||||||
client, err := dial()
|
client, err := dial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -454,11 +453,7 @@ func countMallocs(dial func() (*Client, error), t *testing.T) uint64 {
|
||||||
}
|
}
|
||||||
args := &Args{7, 8}
|
args := &Args{7, 8}
|
||||||
reply := new(Reply)
|
reply := new(Reply)
|
||||||
memstats := new(runtime.MemStats)
|
return testing.AllocsPerRun(100, func() {
|
||||||
runtime.ReadMemStats(memstats)
|
|
||||||
mallocs := 0 - memstats.Mallocs
|
|
||||||
const count = 100
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
err := client.Call("Arith.Add", args, reply)
|
err := client.Call("Arith.Add", args, reply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Add: expected no error but got string %q", err.Error())
|
t.Errorf("Add: expected no error but got string %q", err.Error())
|
||||||
|
|
@ -466,18 +461,15 @@ func countMallocs(dial func() (*Client, error), t *testing.T) uint64 {
|
||||||
if reply.C != args.A+args.B {
|
if reply.C != args.A+args.B {
|
||||||
t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
|
t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
runtime.ReadMemStats(memstats)
|
|
||||||
mallocs += memstats.Mallocs
|
|
||||||
return mallocs / count
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCountMallocs(t *testing.T) {
|
func TestCountMallocs(t *testing.T) {
|
||||||
fmt.Printf("mallocs per rpc round trip: %d\n", countMallocs(dialDirect, t))
|
fmt.Printf("mallocs per rpc round trip: %v\n", countMallocs(dialDirect, t))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCountMallocsOverHTTP(t *testing.T) {
|
func TestCountMallocsOverHTTP(t *testing.T) {
|
||||||
fmt.Printf("mallocs per HTTP rpc round trip: %d\n", countMallocs(dialHTTP, t))
|
fmt.Printf("mallocs per HTTP rpc round trip: %v\n", countMallocs(dialHTTP, t))
|
||||||
}
|
}
|
||||||
|
|
||||||
type writeCrasher struct {
|
type writeCrasher struct {
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,6 @@ var wincleantests = []PathTest{
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClean(t *testing.T) {
|
func TestClean(t *testing.T) {
|
||||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
|
||||||
tests := cleantests
|
tests := cleantests
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
for i := range tests {
|
for i := range tests {
|
||||||
|
|
@ -108,20 +107,12 @@ func TestClean(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ms runtime.MemStats
|
for _, test := range tests {
|
||||||
runtime.ReadMemStats(&ms)
|
allocs := testing.AllocsPerRun(100, func() { filepath.Clean(test.result) })
|
||||||
allocs := -ms.Mallocs
|
if allocs > 0 {
|
||||||
const rounds = 100
|
t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs)
|
||||||
for i := 0; i < rounds; i++ {
|
|
||||||
for _, test := range tests {
|
|
||||||
filepath.Clean(test.result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runtime.ReadMemStats(&ms)
|
|
||||||
allocs += ms.Mallocs
|
|
||||||
if allocs >= rounds {
|
|
||||||
t.Errorf("Clean cleaned paths: %d allocations per test round, want zero", allocs/rounds)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sep = filepath.Separator
|
const sep = filepath.Separator
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
package path
|
package path
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -64,7 +63,6 @@ var cleantests = []PathTest{
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClean(t *testing.T) {
|
func TestClean(t *testing.T) {
|
||||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
|
||||||
for _, test := range cleantests {
|
for _, test := range cleantests {
|
||||||
if s := Clean(test.path); s != test.result {
|
if s := Clean(test.path); s != test.result {
|
||||||
t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
|
t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
|
||||||
|
|
@ -74,20 +72,12 @@ func TestClean(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ms runtime.MemStats
|
for _, test := range cleantests {
|
||||||
runtime.ReadMemStats(&ms)
|
allocs := testing.AllocsPerRun(100, func() { Clean(test.result) })
|
||||||
allocs := -ms.Mallocs
|
if allocs > 0 {
|
||||||
const rounds = 100
|
t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs)
|
||||||
for i := 0; i < rounds; i++ {
|
|
||||||
for _, test := range cleantests {
|
|
||||||
Clean(test.result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runtime.ReadMemStats(&ms)
|
|
||||||
allocs += ms.Mallocs
|
|
||||||
if allocs >= rounds {
|
|
||||||
t.Errorf("Clean cleaned paths: %d allocations per test round, want zero", allocs/rounds)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SplitTest struct {
|
type SplitTest struct {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
. "reflect"
|
. "reflect"
|
||||||
"runtime"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -2012,20 +2011,13 @@ func TestAddr(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func noAlloc(t *testing.T, n int, f func(int)) {
|
func noAlloc(t *testing.T, n int, f func(int)) {
|
||||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
i := -1
|
||||||
// once to prime everything
|
allocs := testing.AllocsPerRun(n, func() {
|
||||||
f(-1)
|
f(i)
|
||||||
memstats := new(runtime.MemStats)
|
i++
|
||||||
runtime.ReadMemStats(memstats)
|
})
|
||||||
oldmallocs := memstats.Mallocs
|
if allocs > 0 {
|
||||||
|
t.Errorf("%d iterations: got %v mallocs, want 0", n, allocs)
|
||||||
for j := 0; j < n; j++ {
|
|
||||||
f(j)
|
|
||||||
}
|
|
||||||
runtime.ReadMemStats(memstats)
|
|
||||||
mallocs := memstats.Mallocs - oldmallocs
|
|
||||||
if mallocs > 0 {
|
|
||||||
t.Fatalf("%d mallocs after %d iterations", mallocs, n)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
package strconv_test
|
package strconv_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
|
||||||
. "strconv"
|
. "strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -44,19 +43,10 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCountMallocs(t *testing.T) {
|
func TestCountMallocs(t *testing.T) {
|
||||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
|
||||||
for _, mt := range mallocTest {
|
for _, mt := range mallocTest {
|
||||||
const N = 100
|
allocs := testing.AllocsPerRun(100, mt.fn)
|
||||||
memstats := new(runtime.MemStats)
|
if max := float64(mt.count); allocs > max {
|
||||||
runtime.ReadMemStats(memstats)
|
t.Errorf("%s: %v allocs, want <=%v", mt.desc, allocs, max)
|
||||||
mallocs := 0 - memstats.Mallocs
|
|
||||||
for i := 0; i < N; i++ {
|
|
||||||
mt.fn()
|
|
||||||
}
|
|
||||||
runtime.ReadMemStats(memstats)
|
|
||||||
mallocs += memstats.Mallocs
|
|
||||||
if mallocs/N > uint64(mt.count) {
|
|
||||||
t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
41
src/pkg/testing/allocs.go
Normal file
41
src/pkg/testing/allocs.go
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AllocsPerRun returns the average number of allocations during calls to f.
|
||||||
|
//
|
||||||
|
// To compute the number of allocations, the function will first be run once as
|
||||||
|
// a warm-up. The average number of allocations over the specified number of
|
||||||
|
// runs will then be measured and returned.
|
||||||
|
//
|
||||||
|
// AllocsPerRun sets GOMAXPROCS to 1 during its measurement and will restore
|
||||||
|
// it before returning.
|
||||||
|
func AllocsPerRun(runs int, f func()) (avg float64) {
|
||||||
|
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
||||||
|
|
||||||
|
// Warm up the function
|
||||||
|
f()
|
||||||
|
|
||||||
|
// Measure the starting statistics
|
||||||
|
var memstats runtime.MemStats
|
||||||
|
runtime.ReadMemStats(&memstats)
|
||||||
|
mallocs := 0 - memstats.Mallocs
|
||||||
|
|
||||||
|
// Run the function the specified number of times
|
||||||
|
for i := 0; i < runs; i++ {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the final statistics
|
||||||
|
runtime.ReadMemStats(&memstats)
|
||||||
|
mallocs += memstats.Mallocs
|
||||||
|
|
||||||
|
// Average the mallocs over the runs (not counting the warm-up)
|
||||||
|
return float64(mallocs) / float64(runs)
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -1258,19 +1257,10 @@ var mallocTest = []struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCountMallocs(t *testing.T) {
|
func TestCountMallocs(t *testing.T) {
|
||||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
|
||||||
for _, mt := range mallocTest {
|
for _, mt := range mallocTest {
|
||||||
const N = 100
|
allocs := int(testing.AllocsPerRun(100, mt.fn))
|
||||||
memstats := new(runtime.MemStats)
|
if allocs > mt.count {
|
||||||
runtime.ReadMemStats(memstats)
|
t.Errorf("%s: %d allocs, want %d", mt.desc, allocs, mt.count)
|
||||||
mallocs := 0 - memstats.Mallocs
|
|
||||||
for i := 0; i < N; i++ {
|
|
||||||
mt.fn()
|
|
||||||
}
|
|
||||||
runtime.ReadMemStats(memstats)
|
|
||||||
mallocs += memstats.Mallocs
|
|
||||||
if mallocs/N > uint64(mt.count) {
|
|
||||||
t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue