Skip to content

Commit

Permalink
add Statistics for cache miss rate (#505)
Browse files Browse the repository at this point in the history
* add Statistics for cache miss rate

* separate fast cache & add test

* refactor test
  • Loading branch information
luohaha committed Jun 16, 2022
1 parent abaaacd commit 464e790
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 0 deletions.
14 changes: 14 additions & 0 deletions nodedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,18 @@ func (ndb *nodeDB) GetNode(hash []byte) (*Node, error) {

// Check the cache.
if elem, ok := ndb.nodeCache[string(hash)]; ok {
if ndb.opts.Stat != nil {
ndb.opts.Stat.IncCacheHitCnt()
}
// Already exists. Move to back of nodeCacheQueue.
ndb.nodeCacheQueue.MoveToBack(elem)
return elem.Value.(*Node), nil
}

if ndb.opts.Stat != nil {
ndb.opts.Stat.IncCacheMissCnt()
}

// Doesn't exist, load.
buf, err := ndb.db.Get(ndb.nodeKey(hash))
if err != nil {
Expand Down Expand Up @@ -165,11 +172,18 @@ func (ndb *nodeDB) GetFastNode(key []byte) (*FastNode, error) {

// Check the cache.
if elem, ok := ndb.fastNodeCache[string(key)]; ok {
if ndb.opts.Stat != nil {
ndb.opts.Stat.IncFastCacheHitCnt()
}
// Already exists. Move to back of fastNodeCacheQueue.
ndb.fastNodeCacheQueue.MoveToBack(elem)
return elem.Value.(*FastNode), nil
}

if ndb.opts.Stat != nil {
ndb.opts.Stat.IncFastCacheMissCnt()
}

// Doesn't exist, load.
buf, err := ndb.db.Get(ndb.fastNodeKey(key))
if err != nil {
Expand Down
59 changes: 59 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,61 @@
package iavl

import "sync/atomic"

// Statisc about db runtime state
type Statistics struct {
// Each time GetNode operation hit cache
cacheHitCnt uint64

// Each time GetNode and GetFastNode operation miss cache
cacheMissCnt uint64

// Each time GetFastNode operation hit cache
fastCacheHitCnt uint64

// Each time GetFastNode operation miss cache
fastCacheMissCnt uint64
}

func (stat *Statistics) IncCacheHitCnt() {
atomic.AddUint64(&stat.cacheHitCnt, 1)
}

func (stat *Statistics) IncCacheMissCnt() {
atomic.AddUint64(&stat.cacheMissCnt, 1)
}

func (stat *Statistics) IncFastCacheHitCnt() {
atomic.AddUint64(&stat.fastCacheHitCnt, 1)
}

func (stat *Statistics) IncFastCacheMissCnt() {
atomic.AddUint64(&stat.fastCacheMissCnt, 1)
}

func (stat *Statistics) GetCacheHitCnt() uint64 {
return atomic.LoadUint64(&stat.cacheHitCnt)
}

func (stat *Statistics) GetCacheMissCnt() uint64 {
return atomic.LoadUint64(&stat.cacheMissCnt)
}

func (stat *Statistics) GetFastCacheHitCnt() uint64 {
return atomic.LoadUint64(&stat.fastCacheHitCnt)
}

func (stat *Statistics) GetFastCacheMissCnt() uint64 {
return atomic.LoadUint64(&stat.fastCacheMissCnt)
}

func (stat *Statistics) Reset() {
atomic.StoreUint64(&stat.cacheHitCnt, 0)
atomic.StoreUint64(&stat.cacheMissCnt, 0)
atomic.StoreUint64(&stat.fastCacheHitCnt, 0)
atomic.StoreUint64(&stat.fastCacheMissCnt, 0)
}

// Options define tree options.
type Options struct {
// Sync synchronously flushes all writes to storage, using e.g. the fsync syscall.
Expand All @@ -10,6 +66,9 @@ type Options struct {
// this, an error is returned when loading the tree. Only used for the initial SaveVersion()
// call.
InitialVersion uint64

// When Stat is not nil, statistical logic needs to be executed
Stat *Statistics
}

// DefaultOptions returns the default options for IAVL.
Expand Down
49 changes: 49 additions & 0 deletions tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1982,3 +1982,52 @@ func Benchmark_GetByIndex(b *testing.B) {
}
})
}

func TestNodeCacheStatisic(t *testing.T) {
db, err := db.NewDB("test", db.MemDBBackend, "")
require.NoError(t, err)
const numKeyVals = 100000
testcases := map[string]struct {
cacheSize int
expectFastCacheHitCnt int
expectFastCacheMissCnt int
expectCacheHitCnt int
expectCacheMissCnt int
}{
"with_cache": {numKeyVals, numKeyVals, 0, 1, 0},
"without_cache": {0, 0, numKeyVals, 0, 1},
}

for name, tc := range testcases {
funcT := func(sub *testing.T) {

stat := &Statistics{}
opts := &Options{Stat: stat}
mt, err := NewMutableTreeWithOpts(db, tc.cacheSize, opts)
require.NoError(t, err)

for i := 0; i < numKeyVals; i++ {
key := []byte(strconv.Itoa(i))
_, err := mt.Set(key, randBytes(10))
require.NoError(t, err)
}
_, ver, _ := mt.SaveVersion()
it, err := mt.GetImmutable(ver)
require.NoError(t, err)

for i := 0; i < numKeyVals; i++ {
key := []byte(strconv.Itoa(i))
val, err := it.Get(key)
require.NoError(t, err)
require.NotNil(t, val)
require.NotEmpty(t, val)
}
require.Equal(t, tc.expectFastCacheHitCnt, int(opts.Stat.GetFastCacheHitCnt()))
require.Equal(t, tc.expectFastCacheMissCnt, int(opts.Stat.GetFastCacheMissCnt()))
require.Equal(t, tc.expectCacheHitCnt, int(opts.Stat.GetCacheHitCnt()))
require.Equal(t, tc.expectCacheMissCnt, int(opts.Stat.GetCacheMissCnt()))
}
t.Run(name, funcT)
}

}

0 comments on commit 464e790

Please sign in to comment.