From 0f0c6a111f35830d85a0b6011b67291897f8169c Mon Sep 17 00:00:00 2001 From: Eitan Yarmush Date: Fri, 12 Apr 2024 18:53:57 +0000 Subject: [PATCH 1/3] resource snapshot v2 with slice --- pkg/resource/snapshot.go | 6 +- pkg/resource/v2/snapshot.go | 176 ++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 pkg/resource/v2/snapshot.go diff --git a/pkg/resource/snapshot.go b/pkg/resource/snapshot.go index 0c5442ee1..c29a8fdb5 100644 --- a/pkg/resource/snapshot.go +++ b/pkg/resource/snapshot.go @@ -62,7 +62,7 @@ func (s Snapshot) cloneInternal(deepCopy bool, selectors ...GVKSelectorFunc) Sna if deepCopy { clone[k] = copyNnsMap(v) } else { - clone[k] = map[types.NamespacedName]client.Object{} + clone[k] = make(map[types.NamespacedName]client.Object, len(v)) maps.Copy(clone[k], v) } continue @@ -78,7 +78,7 @@ func (s Snapshot) cloneInternal(deepCopy bool, selectors ...GVKSelectorFunc) Sna if deepCopy { clone[k] = copyNnsMap(v) } else { - clone[k] = map[types.NamespacedName]client.Object{} + clone[k] = make(map[types.NamespacedName]client.Object, len(v)) maps.Copy(clone[k], v) } continue @@ -127,7 +127,7 @@ func (s ClusterSnapshot) ForEachObject( } func copyNnsMap(m map[types.NamespacedName]client.Object) map[types.NamespacedName]client.Object { - nnsMapCopy := map[types.NamespacedName]client.Object{} + nnsMapCopy := make(map[types.NamespacedName]client.Object, len(m)) for k, v := range m { nnsMapCopy[k] = v.DeepCopyObject().(client.Object) } diff --git a/pkg/resource/v2/snapshot.go b/pkg/resource/v2/snapshot.go new file mode 100644 index 000000000..c836bc14a --- /dev/null +++ b/pkg/resource/v2/snapshot.go @@ -0,0 +1,176 @@ +package v2 + +import ( + "slices" + "strings" + + "github.com/solo-io/skv2/pkg/ezkube" + "github.com/solo-io/skv2/pkg/resource" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type GVKSelectorFunc = resource.GVKSelectorFunc + +// Snapshot represents a generic snapshot of client.Objects scoped to a single cluster +type Snapshot map[schema.GroupVersionKind][]client.Object + +func (s Snapshot) Insert(gvk schema.GroupVersionKind, obj client.Object) { + objects, ok := s[gvk] + if !ok { + objects = []client.Object{} + } + insertIndex, found := slices.BinarySearchFunc( + objects, + obj, + func(a, b client.Object) int { return ezkube.ResourceIdsCompare(a, b) }, + ) + if found { + objects[insertIndex] = obj + } else { + objects = slices.Insert(objects, insertIndex, obj) + } + s[gvk] = objects +} + +func (s Snapshot) Delete(gvk schema.GroupVersionKind, id types.NamespacedName) { + resources, ok := s[gvk] + if !ok { + return + } + + i, found := slices.BinarySearchFunc( + resources, + id, + func(a client.Object, b types.NamespacedName) int { + // compare names + if cmp := strings.Compare(a.GetName(), b.Name); cmp != 0 { + return cmp + } + + // compare namespaces + return strings.Compare(a.GetNamespace(), b.Namespace) + }, + ) + if found { + resources = slices.Delete(resources, i, i+1) + } +} + +func (s Snapshot) ForEachObject(handleObject func(gvk schema.GroupVersionKind, obj client.Object)) { + if s == nil { + return + } + for gvk, objs := range s { + for _, obj := range objs { + handleObject(gvk, obj) + } + } +} + +func (s Snapshot) Clone(selectors ...GVKSelectorFunc) Snapshot { + return s.cloneInternal(true, selectors...) +} + +func (s Snapshot) ShallowCopy(selectors ...GVKSelectorFunc) Snapshot { + return s.cloneInternal(false, selectors...) +} + +func (s Snapshot) cloneInternal(deepCopy bool, selectors ...GVKSelectorFunc) Snapshot { + clone := Snapshot{} + for k, v := range s { + if len(selectors) == 0 { + if deepCopy { + clone[k] = copyNnsSlice(v) + } else { + clone[k] = make([]client.Object, len(v)) + copy(clone[k], v) + } + continue + } + selected := false + for _, selector := range selectors { + if selector(k) { + selected = true + break + } + } + if selected { + if deepCopy { + clone[k] = copyNnsSlice(v) + } else { + clone[k] = make([]client.Object, len(v)) + copy(clone[k], v) + } + continue + } + } + return clone +} + +// ClusterSnapshot represents a set of snapshots partitioned by cluster +type ClusterSnapshot map[string]Snapshot + +func (s ClusterSnapshot) ForEachObject( + handleObject func( + cluster string, + gvk schema.GroupVersionKind, + obj client.Object, + ), +) { + if s == nil { + return + } + for cluster, snap := range s { + snap.ForEachObject(func(gvk schema.GroupVersionKind, obj client.Object) { + handleObject(cluster, gvk, obj) + }) + } +} + +func copyNnsSlice(m []client.Object) []client.Object { + nsSliceCopy := make([]client.Object, len(m)) + for k, v := range m { + nsSliceCopy[k] = v.DeepCopyObject().(client.Object) + } + return nsSliceCopy +} + +func (cs ClusterSnapshot) Insert(cluster string, gvk schema.GroupVersionKind, obj client.Object) { + snapshot, ok := cs[cluster] + if !ok { + snapshot = Snapshot{} + } + snapshot.Insert(gvk, obj) + cs[cluster] = snapshot +} + +func (cs ClusterSnapshot) Delete( + cluster string, + gvk schema.GroupVersionKind, + id types.NamespacedName, +) { + snapshot, ok := cs[cluster] + if !ok { + return + } + snapshot.Delete(gvk, id) + cs[cluster] = snapshot +} + +func (cs ClusterSnapshot) Clone(selectors ...GVKSelectorFunc) ClusterSnapshot { + clone := ClusterSnapshot{} + for k, v := range cs { + clone[k] = v.Clone(selectors...) + } + return clone +} + +func (cs ClusterSnapshot) ShallowCopy(selectors ...GVKSelectorFunc) ClusterSnapshot { + clone := ClusterSnapshot{} + for k, v := range cs { + clone[k] = v.ShallowCopy(selectors...) + } + return clone +} From 5c44360767ef18c1f2f680337e4ba508515f7093 Mon Sep 17 00:00:00 2001 From: Eitan Yarmush Date: Tue, 16 Apr 2024 14:52:39 +0000 Subject: [PATCH 2/3] changelog --- changelog/v0.38.2/less-snapshot-allocs.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/v0.38.2/less-snapshot-allocs.yaml diff --git a/changelog/v0.38.2/less-snapshot-allocs.yaml b/changelog/v0.38.2/less-snapshot-allocs.yaml new file mode 100644 index 000000000..444383801 --- /dev/null +++ b/changelog/v0.38.2/less-snapshot-allocs.yaml @@ -0,0 +1,3 @@ +changelog: + - type: NON_USER_FACING + description: Use make + len for snapshot copies to avoid allocations from growing the maps. From 9125716bb4b4cf35a53cabedf4702eac51a59239 Mon Sep 17 00:00:00 2001 From: Eitan Yarmush Date: Tue, 16 Apr 2024 15:00:28 +0000 Subject: [PATCH 3/3] removed v2 --- pkg/resource/v2/snapshot.go | 176 ------------------------------------ 1 file changed, 176 deletions(-) delete mode 100644 pkg/resource/v2/snapshot.go diff --git a/pkg/resource/v2/snapshot.go b/pkg/resource/v2/snapshot.go deleted file mode 100644 index c836bc14a..000000000 --- a/pkg/resource/v2/snapshot.go +++ /dev/null @@ -1,176 +0,0 @@ -package v2 - -import ( - "slices" - "strings" - - "github.com/solo-io/skv2/pkg/ezkube" - "github.com/solo-io/skv2/pkg/resource" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type GVKSelectorFunc = resource.GVKSelectorFunc - -// Snapshot represents a generic snapshot of client.Objects scoped to a single cluster -type Snapshot map[schema.GroupVersionKind][]client.Object - -func (s Snapshot) Insert(gvk schema.GroupVersionKind, obj client.Object) { - objects, ok := s[gvk] - if !ok { - objects = []client.Object{} - } - insertIndex, found := slices.BinarySearchFunc( - objects, - obj, - func(a, b client.Object) int { return ezkube.ResourceIdsCompare(a, b) }, - ) - if found { - objects[insertIndex] = obj - } else { - objects = slices.Insert(objects, insertIndex, obj) - } - s[gvk] = objects -} - -func (s Snapshot) Delete(gvk schema.GroupVersionKind, id types.NamespacedName) { - resources, ok := s[gvk] - if !ok { - return - } - - i, found := slices.BinarySearchFunc( - resources, - id, - func(a client.Object, b types.NamespacedName) int { - // compare names - if cmp := strings.Compare(a.GetName(), b.Name); cmp != 0 { - return cmp - } - - // compare namespaces - return strings.Compare(a.GetNamespace(), b.Namespace) - }, - ) - if found { - resources = slices.Delete(resources, i, i+1) - } -} - -func (s Snapshot) ForEachObject(handleObject func(gvk schema.GroupVersionKind, obj client.Object)) { - if s == nil { - return - } - for gvk, objs := range s { - for _, obj := range objs { - handleObject(gvk, obj) - } - } -} - -func (s Snapshot) Clone(selectors ...GVKSelectorFunc) Snapshot { - return s.cloneInternal(true, selectors...) -} - -func (s Snapshot) ShallowCopy(selectors ...GVKSelectorFunc) Snapshot { - return s.cloneInternal(false, selectors...) -} - -func (s Snapshot) cloneInternal(deepCopy bool, selectors ...GVKSelectorFunc) Snapshot { - clone := Snapshot{} - for k, v := range s { - if len(selectors) == 0 { - if deepCopy { - clone[k] = copyNnsSlice(v) - } else { - clone[k] = make([]client.Object, len(v)) - copy(clone[k], v) - } - continue - } - selected := false - for _, selector := range selectors { - if selector(k) { - selected = true - break - } - } - if selected { - if deepCopy { - clone[k] = copyNnsSlice(v) - } else { - clone[k] = make([]client.Object, len(v)) - copy(clone[k], v) - } - continue - } - } - return clone -} - -// ClusterSnapshot represents a set of snapshots partitioned by cluster -type ClusterSnapshot map[string]Snapshot - -func (s ClusterSnapshot) ForEachObject( - handleObject func( - cluster string, - gvk schema.GroupVersionKind, - obj client.Object, - ), -) { - if s == nil { - return - } - for cluster, snap := range s { - snap.ForEachObject(func(gvk schema.GroupVersionKind, obj client.Object) { - handleObject(cluster, gvk, obj) - }) - } -} - -func copyNnsSlice(m []client.Object) []client.Object { - nsSliceCopy := make([]client.Object, len(m)) - for k, v := range m { - nsSliceCopy[k] = v.DeepCopyObject().(client.Object) - } - return nsSliceCopy -} - -func (cs ClusterSnapshot) Insert(cluster string, gvk schema.GroupVersionKind, obj client.Object) { - snapshot, ok := cs[cluster] - if !ok { - snapshot = Snapshot{} - } - snapshot.Insert(gvk, obj) - cs[cluster] = snapshot -} - -func (cs ClusterSnapshot) Delete( - cluster string, - gvk schema.GroupVersionKind, - id types.NamespacedName, -) { - snapshot, ok := cs[cluster] - if !ok { - return - } - snapshot.Delete(gvk, id) - cs[cluster] = snapshot -} - -func (cs ClusterSnapshot) Clone(selectors ...GVKSelectorFunc) ClusterSnapshot { - clone := ClusterSnapshot{} - for k, v := range cs { - clone[k] = v.Clone(selectors...) - } - return clone -} - -func (cs ClusterSnapshot) ShallowCopy(selectors ...GVKSelectorFunc) ClusterSnapshot { - clone := ClusterSnapshot{} - for k, v := range cs { - clone[k] = v.ShallowCopy(selectors...) - } - return clone -}