k8s.io/apiserver@v0.31.1/pkg/endpoints/handlers/fieldmanager/bench_test.go (about)

     1  /*
     2  Copyright 2023 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package fieldmanager_test
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"path/filepath"
    24  	"strings"
    25  	"testing"
    26  
    27  	corev1 "k8s.io/api/core/v1"
    28  	"k8s.io/apimachinery/pkg/api/meta"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/runtime/schema"
    33  	"k8s.io/apimachinery/pkg/runtime/serializer"
    34  	"k8s.io/apimachinery/pkg/util/managedfields"
    35  	"k8s.io/apimachinery/pkg/util/managedfields/managedfieldstest"
    36  	"k8s.io/kube-openapi/pkg/validation/spec"
    37  	"sigs.k8s.io/yaml"
    38  )
    39  
    40  var fakeTypeConverter = func() managedfields.TypeConverter {
    41  	data, err := ioutil.ReadFile(filepath.Join(strings.Repeat(".."+string(filepath.Separator), 8),
    42  		"api", "openapi-spec", "swagger.json"))
    43  	if err != nil {
    44  		panic(err)
    45  	}
    46  	swagger := spec.Swagger{}
    47  	if err := json.Unmarshal(data, &swagger); err != nil {
    48  		panic(err)
    49  	}
    50  	definitions := map[string]*spec.Schema{}
    51  	for k, v := range swagger.Definitions {
    52  		p := v
    53  		definitions[k] = &p
    54  	}
    55  	typeConverter, err := managedfields.NewTypeConverter(definitions, false)
    56  	if err != nil {
    57  		panic(err)
    58  	}
    59  	return typeConverter
    60  }()
    61  
    62  func getObjectBytes(file string) []byte {
    63  	s, err := ioutil.ReadFile(file)
    64  	if err != nil {
    65  		panic(err)
    66  	}
    67  	return s
    68  }
    69  
    70  func BenchmarkNewObject(b *testing.B) {
    71  	tests := []struct {
    72  		gvk schema.GroupVersionKind
    73  		obj []byte
    74  	}{
    75  		{
    76  			gvk: schema.FromAPIVersionAndKind("v1", "Pod"),
    77  			obj: getObjectBytes("pod.yaml"),
    78  		},
    79  		{
    80  			gvk: schema.FromAPIVersionAndKind("v1", "Node"),
    81  			obj: getObjectBytes("node.yaml"),
    82  		},
    83  		{
    84  			gvk: schema.FromAPIVersionAndKind("v1", "Endpoints"),
    85  			obj: getObjectBytes("endpoints.yaml"),
    86  		},
    87  	}
    88  	scheme := runtime.NewScheme()
    89  	if err := corev1.AddToScheme(scheme); err != nil {
    90  		b.Fatalf("Failed to add to scheme: %v", err)
    91  	}
    92  	for _, test := range tests {
    93  		b.Run(test.gvk.Kind, func(b *testing.B) {
    94  			f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, test.gvk)
    95  
    96  			decoder := serializer.NewCodecFactory(scheme).UniversalDecoder(test.gvk.GroupVersion())
    97  			newObj, err := runtime.Decode(decoder, test.obj)
    98  			if err != nil {
    99  				b.Fatalf("Failed to parse yaml object: %v", err)
   100  			}
   101  			objMeta, err := meta.Accessor(newObj)
   102  			if err != nil {
   103  				b.Fatalf("Failed to get object meta: %v", err)
   104  			}
   105  			objMeta.SetManagedFields([]metav1.ManagedFieldsEntry{
   106  				{
   107  					Manager:    "default",
   108  					Operation:  "Update",
   109  					APIVersion: "v1",
   110  				},
   111  			})
   112  			appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   113  			if err := yaml.Unmarshal(test.obj, &appliedObj.Object); err != nil {
   114  				b.Fatalf("Failed to parse yaml object: %v", err)
   115  			}
   116  			b.Run("Update", func(b *testing.B) {
   117  				b.ReportAllocs()
   118  				b.ResetTimer()
   119  				for n := 0; n < b.N; n++ {
   120  					if err := f.Update(newObj, "fieldmanager_test"); err != nil {
   121  						b.Fatal(err)
   122  					}
   123  					f.Reset()
   124  				}
   125  			})
   126  			b.Run("UpdateTwice", func(b *testing.B) {
   127  				b.ReportAllocs()
   128  				b.ResetTimer()
   129  				for n := 0; n < b.N; n++ {
   130  					if err := f.Update(newObj, "fieldmanager_test"); err != nil {
   131  						b.Fatal(err)
   132  					}
   133  					if err := f.Update(newObj, "fieldmanager_test_2"); err != nil {
   134  						b.Fatal(err)
   135  					}
   136  					f.Reset()
   137  				}
   138  			})
   139  			b.Run("Apply", func(b *testing.B) {
   140  				b.ReportAllocs()
   141  				b.ResetTimer()
   142  				for n := 0; n < b.N; n++ {
   143  					if err := f.Apply(appliedObj, "fieldmanager_test", false); err != nil {
   144  						b.Fatal(err)
   145  					}
   146  					f.Reset()
   147  				}
   148  			})
   149  			b.Run("UpdateApply", func(b *testing.B) {
   150  				b.ReportAllocs()
   151  				b.ResetTimer()
   152  				for n := 0; n < b.N; n++ {
   153  					if err := f.Update(newObj, "fieldmanager_test"); err != nil {
   154  						b.Fatal(err)
   155  					}
   156  					if err := f.Apply(appliedObj, "fieldmanager_test", false); err != nil {
   157  						b.Fatal(err)
   158  					}
   159  					f.Reset()
   160  				}
   161  			})
   162  		})
   163  	}
   164  }
   165  
   166  func toUnstructured(b *testing.B, o runtime.Object) *unstructured.Unstructured {
   167  	u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(o)
   168  	if err != nil {
   169  		b.Fatalf("Failed to unmarshal to json: %v", err)
   170  	}
   171  	return &unstructured.Unstructured{Object: u}
   172  }
   173  
   174  func BenchmarkConvertObjectToTyped(b *testing.B) {
   175  	tests := []struct {
   176  		gvk schema.GroupVersionKind
   177  		obj []byte
   178  	}{
   179  		{
   180  			gvk: schema.FromAPIVersionAndKind("v1", "Pod"),
   181  			obj: getObjectBytes("pod.yaml"),
   182  		},
   183  		{
   184  			gvk: schema.FromAPIVersionAndKind("v1", "Node"),
   185  			obj: getObjectBytes("node.yaml"),
   186  		},
   187  		{
   188  			gvk: schema.FromAPIVersionAndKind("v1", "Endpoints"),
   189  			obj: getObjectBytes("endpoints.yaml"),
   190  		},
   191  	}
   192  	scheme := runtime.NewScheme()
   193  	if err := corev1.AddToScheme(scheme); err != nil {
   194  		b.Fatalf("Failed to add to scheme: %v", err)
   195  	}
   196  
   197  	for _, test := range tests {
   198  		b.Run(test.gvk.Kind, func(b *testing.B) {
   199  			decoder := serializer.NewCodecFactory(scheme).UniversalDecoder(test.gvk.GroupVersion())
   200  			structured, err := runtime.Decode(decoder, test.obj)
   201  			if err != nil {
   202  				b.Fatalf("Failed to parse yaml object: %v", err)
   203  			}
   204  			b.Run("structured", func(b *testing.B) {
   205  				b.ReportAllocs()
   206  				b.RunParallel(func(pb *testing.PB) {
   207  					for pb.Next() {
   208  						_, err := fakeTypeConverter.ObjectToTyped(structured)
   209  						if err != nil {
   210  							b.Errorf("Error in ObjectToTyped: %v", err)
   211  						}
   212  					}
   213  				})
   214  			})
   215  
   216  			unstructured := toUnstructured(b, structured)
   217  			b.Run("unstructured", func(b *testing.B) {
   218  				b.ReportAllocs()
   219  				b.RunParallel(func(pb *testing.PB) {
   220  					for pb.Next() {
   221  						_, err := fakeTypeConverter.ObjectToTyped(unstructured)
   222  						if err != nil {
   223  							b.Errorf("Error in ObjectToTyped: %v", err)
   224  						}
   225  					}
   226  				})
   227  			})
   228  		})
   229  	}
   230  }
   231  
   232  func BenchmarkCompare(b *testing.B) {
   233  	tests := []struct {
   234  		gvk schema.GroupVersionKind
   235  		obj []byte
   236  	}{
   237  		{
   238  			gvk: schema.FromAPIVersionAndKind("v1", "Pod"),
   239  			obj: getObjectBytes("pod.yaml"),
   240  		},
   241  		{
   242  			gvk: schema.FromAPIVersionAndKind("v1", "Node"),
   243  			obj: getObjectBytes("node.yaml"),
   244  		},
   245  		{
   246  			gvk: schema.FromAPIVersionAndKind("v1", "Endpoints"),
   247  			obj: getObjectBytes("endpoints.yaml"),
   248  		},
   249  	}
   250  
   251  	scheme := runtime.NewScheme()
   252  	if err := corev1.AddToScheme(scheme); err != nil {
   253  		b.Fatalf("Failed to add to scheme: %v", err)
   254  	}
   255  
   256  	for _, test := range tests {
   257  		b.Run(test.gvk.Kind, func(b *testing.B) {
   258  			decoder := serializer.NewCodecFactory(scheme).UniversalDecoder(test.gvk.GroupVersion())
   259  			structured, err := runtime.Decode(decoder, test.obj)
   260  			if err != nil {
   261  				b.Fatal(err)
   262  			}
   263  			tv1, err := fakeTypeConverter.ObjectToTyped(structured)
   264  			if err != nil {
   265  				b.Errorf("Error in ObjectToTyped: %v", err)
   266  			}
   267  			tv2, err := fakeTypeConverter.ObjectToTyped(structured)
   268  			if err != nil {
   269  				b.Errorf("Error in ObjectToTyped: %v", err)
   270  			}
   271  
   272  			b.Run("structured", func(b *testing.B) {
   273  				b.ReportAllocs()
   274  				for n := 0; n < b.N; n++ {
   275  					_, err = tv1.Compare(tv2)
   276  					if err != nil {
   277  						b.Errorf("Error in ObjectToTyped: %v", err)
   278  					}
   279  				}
   280  			})
   281  
   282  			unstructured := toUnstructured(b, structured)
   283  			utv1, err := fakeTypeConverter.ObjectToTyped(unstructured)
   284  			if err != nil {
   285  				b.Errorf("Error in ObjectToTyped: %v", err)
   286  			}
   287  			utv2, err := fakeTypeConverter.ObjectToTyped(unstructured)
   288  			if err != nil {
   289  				b.Errorf("Error in ObjectToTyped: %v", err)
   290  			}
   291  			b.Run("unstructured", func(b *testing.B) {
   292  				b.ReportAllocs()
   293  				b.RunParallel(func(pb *testing.PB) {
   294  					for pb.Next() {
   295  						_, err = utv1.Compare(utv2)
   296  						if err != nil {
   297  							b.Errorf("Error in ObjectToTyped: %v", err)
   298  						}
   299  					}
   300  				})
   301  			})
   302  		})
   303  	}
   304  }
   305  
   306  func BenchmarkRepeatedUpdate(b *testing.B) {
   307  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"))
   308  	podBytes := getObjectBytes("pod.yaml")
   309  
   310  	var obj *corev1.Pod
   311  	if err := yaml.Unmarshal(podBytes, &obj); err != nil {
   312  		b.Fatalf("Failed to parse yaml object: %v", err)
   313  	}
   314  	obj.Spec.Containers[0].Image = "nginx:latest"
   315  	objs := []*corev1.Pod{obj}
   316  	obj = obj.DeepCopy()
   317  	obj.Spec.Containers[0].Image = "nginx:4.3"
   318  	objs = append(objs, obj)
   319  
   320  	appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   321  	if err := yaml.Unmarshal(podBytes, &appliedObj.Object); err != nil {
   322  		b.Fatalf("error decoding YAML: %v", err)
   323  	}
   324  
   325  	err := f.Apply(appliedObj, "fieldmanager_apply", false)
   326  	if err != nil {
   327  		b.Fatal(err)
   328  	}
   329  
   330  	if err := f.Update(objs[1], "fieldmanager_1"); err != nil {
   331  		b.Fatal(err)
   332  	}
   333  
   334  	b.ReportAllocs()
   335  	b.ResetTimer()
   336  	for n := 0; n < b.N; n++ {
   337  		err := f.Update(objs[n%len(objs)], fmt.Sprintf("fieldmanager_%d", n%len(objs)))
   338  		if err != nil {
   339  			b.Fatal(err)
   340  		}
   341  		f.Reset()
   342  	}
   343  }