Skip to content

Commit

Permalink
Use reflect type as pcache key (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
storozhukBM authored Aug 21, 2022
1 parent fe77858 commit bf4f929
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 28 deletions.
18 changes: 5 additions & 13 deletions immcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ var mapIterPool = &sync.Pool{New: func() interface{} { return &reflect.MapIter{}
const maxPoolCacheSizePerGoroutine = 1024

//nolint:gochecknoglobals // reflectValuePoolCache is global to maximise pools re-use
var reflectValuePoolCache = NewPCache(maxPoolCacheSizePerGoroutine)
var reflectValuePoolCache = newPCache(maxPoolCacheSizePerGoroutine)

func perEntrySnapshot(snapshot *ValueSnapshot, value reflect.Value, options Options) *ValueSnapshot {
iterator := mapIterPool.Get().(*reflect.MapIter)
Expand All @@ -391,17 +391,9 @@ func perEntrySnapshot(snapshot *ValueSnapshot, value reflect.Value, options Opti

mapType := value.Type()
mapKeyType := mapType.Key()
mapKeyTypeName := typeName{
path: mapKeyType.PkgPath(),
name: mapKeyType.Name(),
}
mapValueType := mapType.Elem()
mapValueTypeName := typeName{
path: mapValueType.PkgPath(),
name: mapValueType.Name(),
}

keyProvider, ok := reflectValuePoolCache.Load(mapKeyTypeName)
keyProvider, ok := reflectValuePoolCache.load(mapKeyType)
if !ok {
keyProvider = &sync.Pool{
New: func() interface{} {
Expand All @@ -410,12 +402,12 @@ func perEntrySnapshot(snapshot *ValueSnapshot, value reflect.Value, options Opti
},
}
}
defer reflectValuePoolCache.Store(mapKeyTypeName, keyProvider)
defer reflectValuePoolCache.store(mapKeyType, keyProvider)
keyPool := keyProvider.(*sync.Pool)
k := keyPool.Get().(*reflect.Value)
defer keyPool.Put(k)

valueProvider, ok := reflectValuePoolCache.Load(mapValueTypeName)
valueProvider, ok := reflectValuePoolCache.load(mapValueType)
if !ok {
valueProvider = &sync.Pool{
New: func() interface{} {
Expand All @@ -424,7 +416,7 @@ func perEntrySnapshot(snapshot *ValueSnapshot, value reflect.Value, options Opti
},
}
}
defer reflectValuePoolCache.Store(mapValueTypeName, valueProvider)
defer reflectValuePoolCache.store(mapValueType, valueProvider)
valuePool := valueProvider.(*sync.Pool)
v := valuePool.Get().(*reflect.Value)
defer valuePool.Put(v)
Expand Down
6 changes: 6 additions & 0 deletions immcheck_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,12 @@ func expectPanic(t *testing.T, f func(), expectedError error) string {
defer func() {
actualPanic = recover()
if expectedError != nil {
if actualPanic == nil {
t.Fatalf(
"expected error didn't happen. expected %T(%v)",
expectedError, expectedError,
)
}
if !errors.Is(actualPanic.(error), expectedError) {
t.Fatalf(
"unexpected error type. expected %T(%v); actual: %T(%v)",
Expand Down
26 changes: 11 additions & 15 deletions pcache.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package immcheck

import (
"reflect"
"sync"
)

type typeName struct {
path string
name string
}

type cache map[typeName]interface{}
type cache map[reflect.Type]interface{}

type cacheStripe struct {
cache cache
Expand All @@ -27,20 +23,20 @@ type cacheStripe struct {
//
// All operations run in amortized constant time.
// PCache does its best to cache items inside and do as little synchronization as possible
// but since it is cache, there is no guarantee that PCache won't evict your item after Store.
// but since it is cache, there is no guarantee that PCache won't evict your item after store.
//
// PCache evicts random items if I goroutine local cache achieves maxSizePerGoroutine size.
// PCache cleans itself entirely from time to time.
//
// The zero PCache is invalid. Use NewPCache method to create PCache.
type PCache struct {
type pCache struct {
maxSize int
pool *sync.Pool
}

// NewPCache creates PCache with maxSizePerGoroutine.
func NewPCache(maxSizePerGoroutine uint) *PCache {
return &PCache{
// newPCache creates PCache with maxSizePerGoroutine.
func newPCache(maxSizePerGoroutine uint) *pCache {
return &pCache{
maxSize: int(maxSizePerGoroutine),
pool: &sync.Pool{
New: func() interface{} {
Expand All @@ -52,16 +48,16 @@ func NewPCache(maxSizePerGoroutine uint) *PCache {
}
}

// Load fetches (value, true) from cache associated with key or (nil, false) if it is not present.
func (p *PCache) Load(key typeName) (interface{}, bool) {
// load fetches (value, true) from cache associated with key or (nil, false) if it is not present.
func (p *pCache) load(key reflect.Type) (interface{}, bool) {
stripe := p.pool.Get().(*cacheStripe)
defer p.pool.Put(stripe)
value, ok := stripe.cache[key]
return value, ok
}

// Store stores value for a key in cache.
func (p *PCache) Store(key typeName, value interface{}) {
// store stores value for a key in cache.
func (p *pCache) store(key reflect.Type, value interface{}) {
stripe := p.pool.Get().(*cacheStripe)
defer p.pool.Put(stripe)

Expand Down

0 comments on commit bf4f929

Please sign in to comment.