Add benchmarks for FieldManager handling
We don't have a lot of data on allocations and how much time it takes to run apply or update on objects, so adding some benchmark will help us investigate possible improvements. Kubernetes-commit: 92cf3764f979e63317c8f483d8e841e0358599f4
This commit is contained in:
parent
c925c445dd
commit
2f450e5e39
|
@ -18,15 +18,18 @@ package fieldmanager_test
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type fakeObjectConvertor struct{}
|
||||
|
@ -48,7 +51,7 @@ type fakeObjectDefaulter struct{}
|
|||
|
||||
func (d *fakeObjectDefaulter) Default(in runtime.Object) {}
|
||||
|
||||
func NewTestFieldManager(t *testing.T) *fieldmanager.FieldManager {
|
||||
func NewTestFieldManager() *fieldmanager.FieldManager {
|
||||
gv := schema.GroupVersion{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
|
@ -63,13 +66,13 @@ func NewTestFieldManager(t *testing.T) *fieldmanager.FieldManager {
|
|||
}
|
||||
|
||||
func TestFieldManagerCreation(t *testing.T) {
|
||||
if NewTestFieldManager(t) == nil {
|
||||
if NewTestFieldManager() == nil {
|
||||
t.Fatal("failed to create FieldManager")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyStripsFields(t *testing.T) {
|
||||
f := NewTestFieldManager(t)
|
||||
f := NewTestFieldManager()
|
||||
|
||||
obj := &corev1.Pod{}
|
||||
|
||||
|
@ -114,7 +117,7 @@ func TestApplyStripsFields(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestVersionCheck(t *testing.T) {
|
||||
f := NewTestFieldManager(t)
|
||||
f := NewTestFieldManager()
|
||||
|
||||
obj := &corev1.Pod{}
|
||||
|
||||
|
@ -147,7 +150,7 @@ func TestVersionCheck(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApplyDoesNotStripLabels(t *testing.T) {
|
||||
f := NewTestFieldManager(t)
|
||||
f := NewTestFieldManager()
|
||||
|
||||
obj := &corev1.Pod{}
|
||||
|
||||
|
@ -173,3 +176,266 @@ func TestApplyDoesNotStripLabels(t *testing.T) {
|
|||
t.Fatalf("labels shouldn't get stripped on apply: %v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkApplyNewObject(b *testing.B) {
|
||||
f := NewTestFieldManager()
|
||||
|
||||
obj := &corev1.Pod{}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := f.Apply(obj, []byte(`{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "b",
|
||||
"namespace": "b",
|
||||
"creationTimestamp": "2016-05-19T09:59:00Z",
|
||||
},
|
||||
"map": {
|
||||
"fieldA": 1,
|
||||
"fieldB": 1,
|
||||
"fieldC": 1,
|
||||
"fieldD": 1,
|
||||
"fieldE": 1,
|
||||
"fieldF": 1,
|
||||
"fieldG": 1,
|
||||
"fieldH": 1,
|
||||
"fieldI": 1,
|
||||
"fieldJ": 1,
|
||||
"fieldK": 1,
|
||||
"fieldL": 1,
|
||||
"fieldM": 1,
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"value": true
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}`), "fieldmanager_test", false)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUpdateNewObject(b *testing.B) {
|
||||
f := NewTestFieldManager()
|
||||
|
||||
oldObj := &corev1.Pod{}
|
||||
y := `{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "b",
|
||||
"namespace": "b",
|
||||
"creationTimestamp": "2016-05-19T09:59:00Z",
|
||||
},
|
||||
"map": {
|
||||
"fieldA": 1,
|
||||
"fieldB": 1,
|
||||
"fieldC": 1,
|
||||
"fieldD": 1,
|
||||
"fieldE": 1,
|
||||
"fieldF": 1,
|
||||
"fieldG": 1,
|
||||
"fieldH": 1,
|
||||
"fieldI": 1,
|
||||
"fieldJ": 1,
|
||||
"fieldK": 1,
|
||||
"fieldL": 1,
|
||||
"fieldM": 1,
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"value": true
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
}`
|
||||
newObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||
if err := yaml.Unmarshal([]byte(y), &newObj.Object); err != nil {
|
||||
b.Fatalf("Failed to parse yaml object: %v", err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := f.Update(oldObj, newObj, "fieldmanager_test")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRepeatedUpdate(b *testing.B) {
|
||||
f := NewTestFieldManager()
|
||||
|
||||
var oldObj runtime.Object
|
||||
oldObj = &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||
y1 := `{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "b",
|
||||
"namespace": "b",
|
||||
"creationTimestamp": "2016-05-19T09:59:00Z",
|
||||
},
|
||||
"map": {
|
||||
"fieldA": 1,
|
||||
"fieldB": 1,
|
||||
"fieldC": 1,
|
||||
"fieldD": 1,
|
||||
"fieldE": 1,
|
||||
"fieldF": 1,
|
||||
"fieldG": 1,
|
||||
"fieldH": 1,
|
||||
"fieldI": 1,
|
||||
"fieldJ": 1,
|
||||
"fieldK": 1,
|
||||
"fieldL": 1,
|
||||
"fieldM": 1,
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"value": true
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
}`
|
||||
obj1 := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||
if err := yaml.Unmarshal([]byte(y1), &obj1.Object); err != nil {
|
||||
b.Fatalf("Failed to parse yaml object: %v", err)
|
||||
}
|
||||
y2 := `{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "b",
|
||||
"namespace": "b",
|
||||
"creationTimestamp": "2016-05-19T09:59:00Z",
|
||||
},
|
||||
"map": {
|
||||
"fieldA": 1,
|
||||
"fieldB": 1,
|
||||
"fieldC": 1,
|
||||
"fieldD": 1,
|
||||
"fieldE": 1,
|
||||
"fieldF": 1,
|
||||
"fieldG": 1,
|
||||
"fieldH": 1,
|
||||
"fieldI": 1,
|
||||
"fieldJ": 1,
|
||||
"fieldK": 1,
|
||||
"fieldL": 1,
|
||||
"fieldM": 1,
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"value": false
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
}`
|
||||
obj2 := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||
if err := yaml.Unmarshal([]byte(y2), &obj2.Object); err != nil {
|
||||
b.Fatalf("Failed to parse yaml object: %v", err)
|
||||
}
|
||||
y3 := `{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "b",
|
||||
"namespace": "b",
|
||||
"creationTimestamp": "2016-05-19T09:59:00Z",
|
||||
},
|
||||
"map": {
|
||||
"fieldA": 1,
|
||||
"fieldB": 1,
|
||||
"fieldC": 1,
|
||||
"fieldD": 1,
|
||||
"fieldE": 1,
|
||||
"fieldF": 1,
|
||||
"fieldG": 1,
|
||||
"fieldH": 1,
|
||||
"fieldI": 1,
|
||||
"fieldJ": 1,
|
||||
"fieldK": 1,
|
||||
"fieldL": 1,
|
||||
"fieldM": 1,
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"fieldN": {
|
||||
"value": true
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"fieldO": 1,
|
||||
"fieldP": 1,
|
||||
"fieldQ": 1,
|
||||
"fieldR": 1,
|
||||
"fieldS": 1,
|
||||
},
|
||||
|
||||
}`
|
||||
obj3 := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||
if err := yaml.Unmarshal([]byte(y3), &obj3.Object); err != nil {
|
||||
b.Fatalf("Failed to parse yaml object: %v", err)
|
||||
}
|
||||
|
||||
objs := []*unstructured.Unstructured{obj1, obj2, obj3}
|
||||
|
||||
var err error
|
||||
oldObj, err = f.Update(oldObj, objs[0], "fieldmanager_0")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
oldObj, err = f.Update(oldObj, objs[1], "fieldmanager_1")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
oldObj, err = f.Update(oldObj, objs[2], "fieldmanager_2")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
oldObj, err = f.Update(oldObj, objs[n%3], fmt.Sprintf("fieldmanager_%d", n%3))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/value"
|
||||
)
|
||||
|
||||
// TestFieldsRoundTrip tests that a fields trie can be round tripped as a path set
|
||||
|
@ -107,3 +108,52 @@ func TestSetToFieldsError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSetToFields(b *testing.B) {
|
||||
set := fieldpath.NewSet(
|
||||
fieldpath.MakePathOrDie("foo", 0, "bar", "baz"),
|
||||
fieldpath.MakePathOrDie("foo", 0, "bar", "zot"),
|
||||
fieldpath.MakePathOrDie("foo", 0, "bar"),
|
||||
fieldpath.MakePathOrDie("foo", 0),
|
||||
fieldpath.MakePathOrDie("foo", 1, "bar", "baz"),
|
||||
fieldpath.MakePathOrDie("foo", 1, "bar"),
|
||||
fieldpath.MakePathOrDie("qux", fieldpath.KeyByFields("name", value.StringValue("first"))),
|
||||
fieldpath.MakePathOrDie("qux", fieldpath.KeyByFields("name", value.StringValue("first")), "bar"),
|
||||
fieldpath.MakePathOrDie("qux", fieldpath.KeyByFields("name", value.StringValue("second")), "bar"),
|
||||
)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := SetToFields(*set)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFieldsToSet(b *testing.B) {
|
||||
set := fieldpath.NewSet(
|
||||
fieldpath.MakePathOrDie("foo", 0, "bar", "baz"),
|
||||
fieldpath.MakePathOrDie("foo", 0, "bar", "zot"),
|
||||
fieldpath.MakePathOrDie("foo", 0, "bar"),
|
||||
fieldpath.MakePathOrDie("foo", 0),
|
||||
fieldpath.MakePathOrDie("foo", 1, "bar", "baz"),
|
||||
fieldpath.MakePathOrDie("foo", 1, "bar"),
|
||||
fieldpath.MakePathOrDie("qux", fieldpath.KeyByFields("name", value.StringValue("first"))),
|
||||
fieldpath.MakePathOrDie("qux", fieldpath.KeyByFields("name", value.StringValue("first")), "bar"),
|
||||
fieldpath.MakePathOrDie("qux", fieldpath.KeyByFields("name", value.StringValue("second")), "bar"),
|
||||
)
|
||||
fields, err := SetToFields(*set)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := FieldsToSet(fields)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/typed"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
@ -175,3 +176,59 @@ Final object:
|
|||
%#v`, obj, newObj)
|
||||
}
|
||||
}
|
||||
|
||||
var result typed.TypedValue
|
||||
|
||||
func BenchmarkYAMLToTyped(b *testing.B) {
|
||||
y := `
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.15.4
|
||||
`
|
||||
obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||
if err := yaml.Unmarshal([]byte(y), &obj.Object); err != nil {
|
||||
b.Fatalf("Failed to parse yaml object: %v", err)
|
||||
}
|
||||
|
||||
d, err := fakeSchema.OpenAPISchema()
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to parse OpenAPI schema: %v", err)
|
||||
}
|
||||
m, err := proto.NewOpenAPIData(d)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to build OpenAPI models: %v", err)
|
||||
}
|
||||
|
||||
tc, err := internal.NewTypeConverter(m)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to build TypeConverter: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
var r typed.TypedValue
|
||||
for i := 0; i < b.N; i++ {
|
||||
var err error
|
||||
r, err = tc.ObjectToTyped(obj)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to convert object to typed: %v", err)
|
||||
}
|
||||
}
|
||||
result = r
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue