gvisor/pkg/state/tests/bench_test.go

154 lines
3.7 KiB
Go

// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tests
import (
"bytes"
"context"
"encoding/gob"
"fmt"
"testing"
"gvisor.dev/gvisor/pkg/state"
"gvisor.dev/gvisor/pkg/state/wire"
)
// buildPtrObject builds a benchmark object.
func buildPtrObject(n int) interface{} {
b := new(benchStruct)
for i := 0; i < n; i++ {
b = &benchStruct{B: b}
}
return b
}
// buildMapObject builds a benchmark object.
func buildMapObject(n int) interface{} {
b := new(benchStruct)
m := make(map[int]*benchStruct)
for i := 0; i < n; i++ {
m[i] = b
}
return &m
}
// buildSliceObject builds a benchmark object.
func buildSliceObject(n int) interface{} {
b := new(benchStruct)
s := make([]*benchStruct, 0, n)
for i := 0; i < n; i++ {
s = append(s, b)
}
return &s
}
var allObjects = map[string]struct {
New func(int) interface{}
}{
"ptr": {
New: buildPtrObject,
},
"map": {
New: buildMapObject,
},
"slice": {
New: buildSliceObject,
},
}
func buildObjects(n int, fn func(int) interface{}) (iters int, v interface{}) {
// maxSize is the maximum size of an individual object below. For an N
// larger than this, we start to return multiple objects.
const maxSize = 1024
if n <= maxSize {
return 1, fn(n)
}
iters = (n + maxSize - 1) / maxSize
return iters, fn(maxSize)
}
// gobSave is a version of save using gob (no stats available).
func gobSave(_ context.Context, w wire.Writer, v interface{}) (_ state.Stats, err error) {
enc := gob.NewEncoder(w)
err = enc.Encode(v)
return
}
// gobLoad is a version of load using gob (no stats available).
func gobLoad(_ context.Context, r wire.Reader, v interface{}) (_ state.Stats, err error) {
dec := gob.NewDecoder(r)
err = dec.Decode(v)
return
}
var allAlgos = map[string]struct {
Save func(context.Context, wire.Writer, interface{}) (state.Stats, error)
Load func(context.Context, wire.Reader, interface{}) (state.Stats, error)
MaxPtr int
}{
"state": {
Save: state.Save,
Load: state.Load,
},
"gob": {
Save: gobSave,
Load: gobLoad,
},
}
func BenchmarkEncoding(b *testing.B) {
for objName, objInfo := range allObjects {
for algoName, algoInfo := range allAlgos {
b.Run(fmt.Sprintf("%s/%s", objName, algoName), func(b *testing.B) {
b.StopTimer()
n, v := buildObjects(b.N, objInfo.New)
b.ReportAllocs()
b.StartTimer()
for i := 0; i < n; i++ {
if _, err := algoInfo.Save(context.Background(), discard{}, v); err != nil {
b.Errorf("save failed: %v", err)
}
}
b.StopTimer()
})
}
}
}
func BenchmarkDecoding(b *testing.B) {
for objName, objInfo := range allObjects {
for algoName, algoInfo := range allAlgos {
b.Run(fmt.Sprintf("%s/%s", objName, algoName), func(b *testing.B) {
b.StopTimer()
n, v := buildObjects(b.N, objInfo.New)
buf := new(bytes.Buffer)
if _, err := algoInfo.Save(context.Background(), buf, v); err != nil {
b.Errorf("save failed: %v", err)
}
b.ReportAllocs()
b.StartTimer()
var r bytes.Reader
for i := 0; i < n; i++ {
r.Reset(buf.Bytes())
if _, err := algoInfo.Load(context.Background(), &r, v); err != nil {
b.Errorf("load failed: %v", err)
}
}
b.StopTimer()
})
}
}
}