From 7dea3409d4944b0d61108f9bd1b553d8cfed7e9c Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 30 Jan 2023 10:02:57 -0800 Subject: [PATCH] apiextensions: Pool schemas in SchemaHas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using a `sync.Pool` to re-use the buffers that have to escape in the predicate significantly reduces the number of allocations needed to run the SchemaHas method, as shown in the following example: ``` > benchstat old.bench new.bench name old time/op new time/op delta SchemaHas-8 11.0ms ± 0% 2.1ms ± 1% -80.60% (p=0.008 n=5+5) name old alloc/op new alloc/op delta SchemaHas-8 37.5MB ± 0% 0.0MB ± 0% -100.00% (p=0.008 n=5+5) name old allocs/op new allocs/op delta SchemaHas-8 73.1k ± 0% 0.0k -100.00% (p=0.008 n=5+5) ``` --- .../apiextensions/validation/validation.go | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go index be4252c14c4..d93cc932490 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go @@ -23,6 +23,7 @@ import ( "reflect" "regexp" "strings" + "sync" "unicode" "unicode/utf8" @@ -1364,6 +1365,22 @@ func HasSchemaWith(spec *apiextensions.CustomResourceDefinitionSpec, pred func(s return false } +var schemaPool = sync.Pool{ + New: func() any { + return new(apiextensions.JSONSchemaProps) + }, +} + +func schemaHasRecurse(s *apiextensions.JSONSchemaProps, pred func(s *apiextensions.JSONSchemaProps) bool) bool { + if s == nil { + return false + } + schema := schemaPool.Get().(*apiextensions.JSONSchemaProps) + defer schemaPool.Put(schema) + *schema = *s + return SchemaHas(schema, pred) +} + func SchemaHas(s *apiextensions.JSONSchemaProps, pred func(s *apiextensions.JSONSchemaProps) bool) bool { if s == nil { return false @@ -1374,60 +1391,60 @@ func SchemaHas(s *apiextensions.JSONSchemaProps, pred func(s *apiextensions.JSON } if s.Items != nil { - if s.Items != nil && SchemaHas(s.Items.Schema, pred) { + if s.Items != nil && schemaHasRecurse(s.Items.Schema, pred) { return true } for i := range s.Items.JSONSchemas { - if SchemaHas(&s.Items.JSONSchemas[i], pred) { + if schemaHasRecurse(&s.Items.JSONSchemas[i], pred) { return true } } } for i := range s.AllOf { - if SchemaHas(&s.AllOf[i], pred) { + if schemaHasRecurse(&s.AllOf[i], pred) { return true } } for i := range s.AnyOf { - if SchemaHas(&s.AnyOf[i], pred) { + if schemaHasRecurse(&s.AnyOf[i], pred) { return true } } for i := range s.OneOf { - if SchemaHas(&s.OneOf[i], pred) { + if schemaHasRecurse(&s.OneOf[i], pred) { return true } } - if SchemaHas(s.Not, pred) { + if schemaHasRecurse(s.Not, pred) { return true } for _, s := range s.Properties { - if SchemaHas(&s, pred) { + if schemaHasRecurse(&s, pred) { return true } } if s.AdditionalProperties != nil { - if SchemaHas(s.AdditionalProperties.Schema, pred) { + if schemaHasRecurse(s.AdditionalProperties.Schema, pred) { return true } } for _, s := range s.PatternProperties { - if SchemaHas(&s, pred) { + if schemaHasRecurse(&s, pred) { return true } } if s.AdditionalItems != nil { - if SchemaHas(s.AdditionalItems.Schema, pred) { + if schemaHasRecurse(s.AdditionalItems.Schema, pred) { return true } } for _, s := range s.Definitions { - if SchemaHas(&s, pred) { + if schemaHasRecurse(&s, pred) { return true } } for _, d := range s.Dependencies { - if SchemaHas(d.Schema, pred) { + if schemaHasRecurse(d.Schema, pred) { return true } }