apiextensions: Pool schemas in SchemaHas

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)
```
This commit is contained in:
Antoine Pelisse 2023-01-30 10:02:57 -08:00
parent a7c0467e01
commit 7dea3409d4

View File

@ -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
}
}