From 418095f89b3de921fb9b5d1c298048666873dbfc Mon Sep 17 00:00:00 2001 From: Isaac Jimeno Date: Mon, 27 Nov 2023 18:56:01 +0100 Subject: [PATCH 1/5] Add benchmark with variable deployments --- pkg/controller/generic_reconciler_test.go | 79 +++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/pkg/controller/generic_reconciler_test.go b/pkg/controller/generic_reconciler_test.go index 27a69500..39aac809 100644 --- a/pkg/controller/generic_reconciler_test.go +++ b/pkg/controller/generic_reconciler_test.go @@ -18,6 +18,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/rand" kubefake "k8s.io/client-go/kubernetes/fake" "sigs.k8s.io/controller-runtime/pkg/client" clifake "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -1046,3 +1047,81 @@ func createTestReconciler(scheme *runtime.Scheme, gvks []schema.GroupVersionKind testGenericReconciler.resourceGVKs = gvks return testGenericReconciler, nil } + +func BenchmarkGroupAppObjects(b *testing.B) { + namespace := "test" + deploymentNumber := 100 + channelBuffer := 10 + + // Given + objs := []client.Object{ + &policyv1.PodDisruptionBudget{ + ObjectMeta: metav1.ObjectMeta{Name: "test-pdb-A-B-C", Namespace: namespace}, + Spec: policyv1.PodDisruptionBudgetSpec{ + Selector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "app", Operator: metav1.LabelSelectorOpIn, Values: []string{"A", "B", "C"}, + }, + }, + }, + }, + }, + &policyv1.PodDisruptionBudget{ + ObjectMeta: metav1.ObjectMeta{Name: "test-pdb-not-in-C", Namespace: namespace}, + Spec: policyv1.PodDisruptionBudgetSpec{ + Selector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "app", Operator: metav1.LabelSelectorOpNotIn, Values: []string{"C"}, + }, + }, + }, + }, + }, + } + + objs = append(objs, generateDeployments(deploymentNumber, namespace)...) + + gvks := []schema.GroupVersionKind{ + { + Group: "policy", Kind: "PodDisruptionBudget", Version: "v1", + }, + { + Group: "apps", Kind: "Deployment", Version: "v1", + }, + } + + // When + gr, err := createTestReconciler(nil, gvks, objs) + assert.NoError(b, err) + + // Benchmark + for i := 0; i < b.N; i++ { + ch := make(chan groupOfObjects, channelBuffer) + go gr.groupAppObjects(context.Background(), namespace, ch) + + for group := range ch { + b.Logf("Received group: %s, with %d objects", group.label, len(group.objects)) + } + } +} + +// generateDeployments is a helper function for benchmark to create iterative deployments +func generateDeployments(count int, namespace string) []client.Object { + objects := make([]client.Object, count) + + for i := 0; i < count; i++ { + objects[i] = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("test-deployment-%d", i), + Namespace: namespace, + Labels: map[string]string{ + "app": fmt.Sprintf("App-%s", []string{"A", "B", "C", "D"}[rand.Intn(4)]), + }, + }, + } + } + + return objects +} From 028eb4357d81fbcfbf76b2f3bff70f859c3b31ae Mon Sep 17 00:00:00 2001 From: Isaac Jimeno Date: Fri, 1 Dec 2023 12:14:24 +0100 Subject: [PATCH 2/5] Fix linting --- pkg/controller/generic_reconciler_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/controller/generic_reconciler_test.go b/pkg/controller/generic_reconciler_test.go index 39aac809..f51bd9c2 100644 --- a/pkg/controller/generic_reconciler_test.go +++ b/pkg/controller/generic_reconciler_test.go @@ -1061,7 +1061,8 @@ func BenchmarkGroupAppObjects(b *testing.B) { Selector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ { - Key: "app", Operator: metav1.LabelSelectorOpIn, Values: []string{"A", "B", "C"}, + Key: "app", Operator: metav1.LabelSelectorOpIn, + Values: []string{"A", "B", "C"}, }, }, }, @@ -1073,7 +1074,8 @@ func BenchmarkGroupAppObjects(b *testing.B) { Selector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ { - Key: "app", Operator: metav1.LabelSelectorOpNotIn, Values: []string{"C"}, + Key: "app", Operator: metav1.LabelSelectorOpNotIn, + Values: []string{"C"}, }, }, }, From 9766466a5c10a3dc65a5f0a9a58725ad10b0826f Mon Sep 17 00:00:00 2001 From: Isaac Jimeno Date: Mon, 11 Dec 2023 18:43:30 +0100 Subject: [PATCH 3/5] Add benchmark doc --- pkg/controller/generic_reconciler_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pkg/controller/generic_reconciler_test.go b/pkg/controller/generic_reconciler_test.go index f51bd9c2..ae5038e7 100644 --- a/pkg/controller/generic_reconciler_test.go +++ b/pkg/controller/generic_reconciler_test.go @@ -1048,6 +1048,25 @@ func createTestReconciler(scheme *runtime.Scheme, gvks []schema.GroupVersionKind return testGenericReconciler, nil } +// BenchmarkGroupAppObjects measures the performance of grouping Kubernetes objects based on their labels. +// The benchmark focuses on a scenario where a Reconciler needs to group different objects based on the 'app' label. +// # Benchmark configuration: +// +// The benchmark is configured with the following parameters: +// - namespace: "test" - The namespace in which the objects will be created. +// - deploymentNumber: 100 - The number of Deployment objects to create to test. +// - channelBuffer: 10 - The buffer size for the channel used in the grouping process. +// +// # How to run: +// +// To run the benchmark, run the following command: +// +// go test -bench=BenchmarkGroupAppObjects +// +// Adjust the number of deployments or the buffer to match a given scenario. +// Note that the local benchmark can be modified by other processes running in the background. +// If the 'groupAppObjects' function is modified, run some benchmarks with before and after status +// to compare performance improvements. func BenchmarkGroupAppObjects(b *testing.B) { namespace := "test" deploymentNumber := 100 From 736b93bd12c55c6faed50c4af6d18b59099ac2ff Mon Sep 17 00:00:00 2001 From: Isaac Jimeno Date: Tue, 12 Dec 2023 17:19:49 +0100 Subject: [PATCH 4/5] Fix deployments labels --- pkg/controller/generic_reconciler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/generic_reconciler_test.go b/pkg/controller/generic_reconciler_test.go index ae5038e7..23de504c 100644 --- a/pkg/controller/generic_reconciler_test.go +++ b/pkg/controller/generic_reconciler_test.go @@ -1138,7 +1138,7 @@ func generateDeployments(count int, namespace string) []client.Object { Name: fmt.Sprintf("test-deployment-%d", i), Namespace: namespace, Labels: map[string]string{ - "app": fmt.Sprintf("App-%s", []string{"A", "B", "C", "D"}[rand.Intn(4)]), + "app": []string{"A", "B", "C", "D"}[rand.Intn(4)], }, }, } From fcc8b3014a109d47e7a4d7e2f9f5403e1c43c791 Mon Sep 17 00:00:00 2001 From: Isaac Jimeno Date: Wed, 13 Dec 2023 09:26:47 +0100 Subject: [PATCH 5/5] Fix benchmark with default memory consumption metrics --- pkg/controller/generic_reconciler_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/controller/generic_reconciler_test.go b/pkg/controller/generic_reconciler_test.go index 23de504c..2295c7c4 100644 --- a/pkg/controller/generic_reconciler_test.go +++ b/pkg/controller/generic_reconciler_test.go @@ -1061,7 +1061,7 @@ func createTestReconciler(scheme *runtime.Scheme, gvks []schema.GroupVersionKind // // To run the benchmark, run the following command: // -// go test -bench=BenchmarkGroupAppObjects +// go test ./pkg/controller/ -bench ^BenchmarkGroupAppObjects$ // // Adjust the number of deployments or the buffer to match a given scenario. // Note that the local benchmark can be modified by other processes running in the background. @@ -1070,7 +1070,6 @@ func createTestReconciler(scheme *runtime.Scheme, gvks []schema.GroupVersionKind func BenchmarkGroupAppObjects(b *testing.B) { namespace := "test" deploymentNumber := 100 - channelBuffer := 10 // Given objs := []client.Object{ @@ -1119,13 +1118,16 @@ func BenchmarkGroupAppObjects(b *testing.B) { // Benchmark for i := 0; i < b.N; i++ { - ch := make(chan groupOfObjects, channelBuffer) + ch := make(chan groupOfObjects) go gr.groupAppObjects(context.Background(), namespace, ch) for group := range ch { b.Logf("Received group: %s, with %d objects", group.label, len(group.objects)) } } + + // This line reports memory consumption automatically (Bytes/operation and number of allocations/op) + b.ReportAllocs() } // generateDeployments is a helper function for benchmark to create iterative deployments