mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 15:58:37 +00:00
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.
This commit is contained in:
parent
5a7e336689
commit
92cf3764f9
@ -47,7 +47,9 @@ go_test(
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ go_test(
|
||||
"//vendor/k8s.io/kube-openapi/pkg/util/proto/testing:go_default_library",
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/fieldpath:go_default_library",
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/merge:go_default_library",
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/typed:go_default_library",
|
||||
"//vendor/sigs.k8s.io/structured-merge-diff/value:go_default_library",
|
||||
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@ -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
Block a user