Skip to content
Snippets Groups Projects
Commit 937efab2 authored by Colin Cross's avatar Colin Cross
Browse files

Use sync.Map for type field cache

The type field cache was using an atomic pointer to an immutable
map, which required copying the entire map each time a cache
entry was added.  Profiling showed that this was a significant
hot spot.  Use sync.Map instead.

Change-Id: Ie7c779c5e9e2be1cd530747d74025dcfd206763a
parent de7afaaf
Branches
No related tags found
No related merge requests found
...@@ -18,7 +18,6 @@ import ( ...@@ -18,7 +18,6 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"sync" "sync"
"sync/atomic"
) )
func CloneProperties(structValue reflect.Value) reflect.Value { func CloneProperties(structValue reflect.Value) reflect.Value {
...@@ -248,28 +247,17 @@ func cloneEmptyProperties(dstValue, srcValue reflect.Value) { ...@@ -248,28 +247,17 @@ func cloneEmptyProperties(dstValue, srcValue reflect.Value) {
} }
} }
var typeFieldCache sync.Map
func typeFields(typ reflect.Type) []reflect.StructField {
// reflect.Type.Field allocates a []int{} to hold the index every time it is called, which ends up // reflect.Type.Field allocates a []int{} to hold the index every time it is called, which ends up
// being a significant portion of the GC pressure. It can't reuse the same one in case a caller // being a significant portion of the GC pressure. It can't reuse the same one in case a caller
// modifies the backing array through the slice. Since we don't modify it, cache the result // modifies the backing array through the slice. Since we don't modify it, cache the result
// locally to reduce allocations. // locally to reduce allocations.
type typeFieldMap map[reflect.Type][]reflect.StructField
var (
// Stores an atomic pointer to map caching Type to its StructField
typeFieldCache atomic.Value
// Lock used by slow path updating the cache pointer
typeFieldCacheWriterLock sync.Mutex
)
func init() {
typeFieldCache.Store(make(typeFieldMap))
}
func typeFields(typ reflect.Type) []reflect.StructField {
// Fast path // Fast path
cache := typeFieldCache.Load().(typeFieldMap) if typeFields, ok := typeFieldCache.Load(typ); ok {
if typeFields, ok := cache[typ]; ok { return typeFields.([]reflect.StructField)
return typeFields
} }
// Slow path // Slow path
...@@ -279,18 +267,7 @@ func typeFields(typ reflect.Type) []reflect.StructField { ...@@ -279,18 +267,7 @@ func typeFields(typ reflect.Type) []reflect.StructField {
typeFields[i] = typ.Field(i) typeFields[i] = typ.Field(i)
} }
typeFieldCacheWriterLock.Lock() typeFieldCache.Store(typ, typeFields)
defer typeFieldCacheWriterLock.Unlock()
old := typeFieldCache.Load().(typeFieldMap)
cache = make(typeFieldMap)
for k, v := range old {
cache[k] = v
}
cache[typ] = typeFields
typeFieldCache.Store(cache)
return typeFields return typeFields
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment