From 9445a776b5454a22ceb8ba2b29e458db8def2478 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Tue, 13 Oct 2020 16:25:05 +0800 Subject: [PATCH 01/24] Use W-TinyLFU algorithm to enhance the LRU cache --- core/hotspot/cache/concurrent_tinylfu.go | 88 ++++++++ .../concurrent_tinylfu_benchmark_test.go | 63 ++++++ core/hotspot/cache/count_min_sketch.go | 85 ++++++++ core/hotspot/cache/doorkeeper.go | 97 +++++++++ core/hotspot/cache/hash.go | 95 +++++++++ core/hotspot/cache/slru.go | 191 ++++++++++++++++++ core/hotspot/cache/tinylfu.go | 185 +++++++++++++++++ core/hotspot/cache/tinylfu_test.go | 86 ++++++++ core/hotspot/rule_manager_test.go | 6 +- core/hotspot/traffic_shaping.go | 6 +- 10 files changed, 896 insertions(+), 6 deletions(-) create mode 100644 core/hotspot/cache/concurrent_tinylfu.go create mode 100644 core/hotspot/cache/concurrent_tinylfu_benchmark_test.go create mode 100644 core/hotspot/cache/count_min_sketch.go create mode 100644 core/hotspot/cache/doorkeeper.go create mode 100644 core/hotspot/cache/hash.go create mode 100644 core/hotspot/cache/slru.go create mode 100644 core/hotspot/cache/tinylfu.go create mode 100644 core/hotspot/cache/tinylfu_test.go diff --git a/core/hotspot/cache/concurrent_tinylfu.go b/core/hotspot/cache/concurrent_tinylfu.go new file mode 100644 index 000000000..70fe371c1 --- /dev/null +++ b/core/hotspot/cache/concurrent_tinylfu.go @@ -0,0 +1,88 @@ +package cache + +import ( + "sync" +) + +// TinyLfuCacheMap use tinyLfu strategy to cache the most frequently accessed hotspot parameter +type TinyLfuCacheMap struct { + // Not thread safe + tinyLfu *TinyLfu + lock *sync.RWMutex +} + +func (c *TinyLfuCacheMap) Add(key interface{}, value *int64) { + c.lock.Lock() + defer c.lock.Unlock() + + c.tinyLfu.Add(key, value) + return +} + +func (c *TinyLfuCacheMap) AddIfAbsent(key interface{}, value *int64) (priorValue *int64) { + c.lock.Lock() + defer c.lock.Unlock() + val := c.tinyLfu.AddIfAbsent(key, value) + if val == nil { + return nil + } + priorValue = val.(*int64) + return +} + +func (c *TinyLfuCacheMap) Get(key interface{}) (value *int64, isFound bool) { + c.lock.Lock() + defer c.lock.Unlock() + + val, found := c.tinyLfu.Get(key) + if found { + return val.(*int64), true + } + return nil, false +} + +func (c *TinyLfuCacheMap) Remove(key interface{}) (isFound bool) { + c.lock.Lock() + defer c.lock.Unlock() + + return c.tinyLfu.Remove(key) +} + +func (c *TinyLfuCacheMap) Contains(key interface{}) (ok bool) { + c.lock.RLock() + defer c.lock.RUnlock() + + return c.tinyLfu.Contains(key) +} + +func (c *TinyLfuCacheMap) Keys() []interface{} { + c.lock.RLock() + defer c.lock.RUnlock() + + return c.tinyLfu.Keys() +} + +func (c *TinyLfuCacheMap) Len() int { + c.lock.RLock() + defer c.lock.RUnlock() + + return c.tinyLfu.Len() +} + +func (c *TinyLfuCacheMap) Purge() { + c.lock.Lock() + defer c.lock.Unlock() + + c.tinyLfu.Purge() +} + +func NewTinyLfuCacheMap(size int) ConcurrentCounterCache { + tinyLfu, err := NewTinyLfu(size) + if err != nil { + return nil + } + return &TinyLfuCacheMap{ + tinyLfu: tinyLfu, + lock: new(sync.RWMutex), + } +} diff --git a/core/hotspot/cache/concurrent_tinylfu_benchmark_test.go b/core/hotspot/cache/concurrent_tinylfu_benchmark_test.go new file mode 100644 index 000000000..9ba6dcc1b --- /dev/null +++ b/core/hotspot/cache/concurrent_tinylfu_benchmark_test.go @@ -0,0 +1,63 @@ +package cache + +import ( + "strconv" + "testing" +) + +const Size = 50000 + +func Benchmark_TINYLFU_AddIfAbsent(b *testing.B) { + c := NewTinyLfuCacheMap(Size) + for a := 1; a <= Size; a++ { + val := new(int64) + *val = int64(a) + c.Add(strconv.Itoa(a), val) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j := 1000; j <= 1001; j++ { + newVal := new(int64) + *newVal = int64(j) + prior := c.AddIfAbsent(strconv.Itoa(j), newVal) + if *prior != int64(j) { + b.Fatal("error!") + } + } + } +} + +func Benchmark_TINYLFU_Add(b *testing.B) { + c := NewTinyLfuCacheMap(Size) + for a := 1; a <= Size; a++ { + val := new(int64) + *val = int64(a) + c.Add(strconv.Itoa(a), val) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j := Size - 100; j <= Size-99; j++ { + newVal := new(int64) + *newVal = int64(j) + c.Add(strconv.Itoa(j), newVal) + } + } +} + +func Benchmark_TINYLFU_Get(b *testing.B) { + c := NewTinyLfuCacheMap(Size) + for a := 1; a <= Size; a++ { + val := new(int64) + *val = int64(a) + c.Add(strconv.Itoa(a), val) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j := Size - 100; j <= Size-99; j++ { + val, found := c.Get(strconv.Itoa(j)) + if *val != int64(j) || !found { + b.Fatal("error") + } + } + } +} diff --git a/core/hotspot/cache/count_min_sketch.go b/core/hotspot/cache/count_min_sketch.go new file mode 100644 index 000000000..5cb41b02a --- /dev/null +++ b/core/hotspot/cache/count_min_sketch.go @@ -0,0 +1,85 @@ +package cache + +const sketchDepth = 4 + +// countMinSketch is an implementation of count-min sketch with 4-bit counters. +type countMinSketch struct { + counters []uint64 + mask uint32 +} + +// init initialize count-min sketch with the given width. +func newCountMinSketch(width int) *countMinSketch { + size := nextPowerOfTwo(uint32(width)) >> 2 + if size < 1 { + size = 1 + } + countMinSketch := countMinSketch{ + make([]uint64, size), + size - 1, + } + return &countMinSketch +} + +// add increase counters with given hash +func (c *countMinSketch) add(h uint64) { + hash1, hash2 := uint32(h), uint32(h>>32) + + for i := uint32(0); i < sketchDepth; i++ { + combinedHash := hash1 + (i * hash2) + idx, off := c.position(combinedHash) + c.inc(idx, (16*i)+off) + } +} + +// estimate returns minimum value of counters associated with the given hash. +func (c *countMinSketch) estimate(h uint64) uint8 { + hash1, hash2 := uint32(h), uint32(h>>32) + + var min uint8 = 0xFF + for i := uint32(0); i < sketchDepth; i++ { + combinedHash := hash1 + (i * hash2) + idx, off := c.position(combinedHash) + count := c.val(idx, (16*i)+off) + if count < min { + min = count + } + } + return min +} + +func (c *countMinSketch) reset() { + for i, v := range c.counters { + if v != 0 { + //divides all by two. + c.counters[i] = (v >> 1) & 0x7777777777777777 + } + } +} + +func (c *countMinSketch) position(h uint32) (idx uint32, off uint32) { + idx = (h >> 2) & c.mask + off = (h & 3) << 2 + return +} + +// inc increases value at index idx. +func (c *countMinSketch) inc(idx, off uint32) { + v := c.counters[idx] + count := uint8(v>>off) & 0x0F + if count < 15 { + c.counters[idx] = v + (1 << off) + } +} + +// val returns value at index idx. +func (c *countMinSketch) val(idx, off uint32) uint8 { + v := c.counters[idx] + return uint8(v>>off) & 0x0F +} + +func (c *countMinSketch) clear() { + for i := range c.counters { + c.counters[i] = 0 + } +} diff --git a/core/hotspot/cache/doorkeeper.go b/core/hotspot/cache/doorkeeper.go new file mode 100644 index 000000000..e3e76aa88 --- /dev/null +++ b/core/hotspot/cache/doorkeeper.go @@ -0,0 +1,97 @@ +package cache + +import "math" + +// doorkeeper is Bloom Filter implementation. +type doorkeeper struct { + // distinct hash functions needed + numHashes uint32 + // size of bit vector in bits + numBits uint32 + // doorkeeper bit vector + bits []uint64 +} + +// init initializes doorkeeper with the given expected insertions ins and +// false positive probability falsePositiveRate. +func newDoorkeeper(ins int, falsePositiveRate float64) *doorkeeper { + numBits := nextPowerOfTwo(uint32(float64(ins) * -math.Log(falsePositiveRate) / (math.Log(2.0) * math.Log(2.0)))) + if numBits < 1024 { + numBits = 1024 + } + d := doorkeeper{} + d.numBits = numBits + + if ins == 0 { + d.numHashes = 2 + } else { + d.numHashes = uint32(math.Log(2.0) * float64(numBits) / float64(ins)) + if d.numHashes < 2 { + d.numHashes = 2 + } + } + + d.bits = make([]uint64, int(numBits+63)/64) + return &d +} + +// put inserts a hash value into the bloom filter. +// returns true if the value may already in the doorkeeper. +func (d *doorkeeper) put(h uint64) bool { + //only protectedLs hash functions are necessary to effectively + //implement a Bloom filter without any loss in the asymptotic false positive probability + //split up 64-bit hashcode into protectedLs 32-bit hashcode + hash1, hash2 := uint32(h), uint32(h>>32) + var o uint = 1 + for i := uint32(0); i < d.numHashes; i++ { + combinedHash := hash1 + (i * hash2) + o &= d.getSet(combinedHash & (d.numBits - 1)) + } + return o == 1 +} + +//contains returns true if the given hash is may be in the filter. +func (d *doorkeeper) contains(h uint64) bool { + h1, h2 := uint32(h), uint32(h>>32) + var o uint = 1 + for i := uint32(0); i < d.numHashes; i++ { + o &= d.get((h1 + (i * h2)) & (d.numBits - 1)) + } + return o == 1 +} + +// set bit at index i and returns previous value. +func (d *doorkeeper) getSet(i uint32) uint { + idx, shift := i/64, i%64 + v := d.bits[idx] + m := uint64(1) << shift + d.bits[idx] |= m + return uint((v & m) >> shift) +} + +// get returns bit set at index i. +func (d *doorkeeper) get(i uint32) uint { + idx, shift := i/64, i%64 + val := d.bits[idx] + mask := uint64(1) << shift + return uint((val & mask) >> shift) +} + +// reset clears the doorkeeper. +func (d *doorkeeper) reset() { + for i := range d.bits { + d.bits[i] = 0 + } +} + +// return the integer >= i which is a power of protectedLs +func nextPowerOfTwo(i uint32) uint32 { + n := i - 1 + n |= n >> 1 + n |= n >> 2 + n |= n >> 4 + n |= n >> 8 + n |= n >> 16 + n++ + return n +} diff --git a/core/hotspot/cache/hash.go b/core/hotspot/cache/hash.go new file mode 100644 index 000000000..e129bca54 --- /dev/null +++ b/core/hotspot/cache/hash.go @@ -0,0 +1,95 @@ +package cache + +import ( + "bytes" + "encoding/binary" + "encoding/gob" + "hash/fnv" + "math" + "reflect" +) + +func sum(k interface{}) uint64 { + switch h := k.(type) { + case int: + return hashU64(uint64(h)) + case int8: + return hashU64(uint64(h)) + case int16: + return hashU64(uint64(h)) + case int32: + return hashU64(uint64(h)) + case int64: + return hashU64(uint64(h)) + case uint: + return hashU64(uint64(h)) + case uint8: + return hashU64(uint64(h)) + case uint16: + return hashU64(uint64(h)) + case uint32: + return hashU64(uint64(h)) + case uint64: + return hashU64(h) + case uintptr: + return hashU64(uint64(h)) + case float32: + return hashU64(uint64(math.Float32bits(h))) + case float64: + return hashU64(math.Float64bits(h)) + case bool: + if h { + return 1 + } + return 0 + case string: + return hashString(h) + } + if h, ok := hashPointer(k); ok { + return h + } + if h, ok := hashWithGob(k); ok { + return h + } + return 0 + +} + +func hashU64(data uint64) uint64 { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, data) + return hashByteArray(b) +} + +func hashString(data string) uint64 { + return hashByteArray([]byte(data)) +} + +func hashWithGob(data interface{}) (uint64, bool) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(data) + if err != nil { + return 0, false + } + return hashByteArray(buf.Bytes()), true +} + +func hashByteArray(bytes []byte) uint64 { + f := fnv.New64() + _, err := f.Write(bytes) + if err != nil { + return 0 + } + return binary.LittleEndian.Uint64(f.Sum(nil)) +} + +func hashPointer(k interface{}) (uint64, bool) { + v := reflect.ValueOf(k) + switch v.Kind() { + case reflect.Ptr, reflect.UnsafePointer, reflect.Func, reflect.Slice, reflect.Map, reflect.Chan: + return hashU64(uint64(v.Pointer())), true + default: + return 0, false + } +} diff --git a/core/hotspot/cache/slru.go b/core/hotspot/cache/slru.go new file mode 100644 index 000000000..6e99228db --- /dev/null +++ b/core/hotspot/cache/slru.go @@ -0,0 +1,191 @@ +package cache + +import "container/list" + +const ( + admissionWindow uint8 = iota + probationSegment + protectedSegment +) + +const protectedRatio = 0.8 + +type slruItem struct { + listId uint8 + key interface{} + value interface{} + keyHash uint64 +} + +// slru is a segmented LRU. +type slru struct { + data map[interface{}]*list.Element + probationCap, protectedCap int + probationLs, protectedLs *list.List +} + +func newSLRU(cap int, data map[interface{}]*list.Element) *slru { + protectedCap := int(float64(cap) * protectedRatio) + probationCap := cap - protectedCap + return &slru{ + data: data, + probationCap: probationCap, + probationLs: list.New(), + protectedCap: protectedCap, + protectedLs: list.New(), + } +} + +// Get looks up a key's value from the cache. +func (slru *slru) get(v *list.Element) { + item := v.Value.(*slruItem) + + if item.listId == protectedSegment { + slru.protectedLs.MoveToFront(v) + return + } + + if slru.protectedLs.Len() < slru.protectedCap { + slru.probationLs.Remove(v) + item.listId = protectedSegment + slru.data[item.key] = slru.protectedLs.PushFront(item) + return + } + + back := slru.protectedLs.Back() + backItem := back.Value.(*slruItem) + + // swap the item + *backItem, *item = *item, *backItem + backItem.listId = protectedSegment + item.listId = probationSegment + slru.data[item.key] = v + slru.data[backItem.key] = back + + // move the elements to the front of their lists + slru.probationLs.MoveToFront(v) + slru.protectedLs.MoveToFront(back) +} + +// add set a value in the cache +func (slru *slru) add(newItem slruItem) { + + newItem.listId = probationSegment + + if slru.probationLs.Len() < slru.probationCap || (slru.Len() < slru.probationCap+slru.protectedCap) { + slru.data[newItem.key] = slru.probationLs.PushFront(&newItem) + return + } + + e := slru.probationLs.Back() + item := e.Value.(*slruItem) + delete(slru.data, item.key) + *item = newItem + + slru.data[item.key] = e + slru.probationLs.MoveToFront(e) +} + +func (slru *slru) victim() *slruItem { + + if slru.Len() < slru.probationCap+slru.protectedCap { + return nil + } + + v := slru.probationLs.Back() + + return v.Value.(*slruItem) +} + +// Len returns the total number of items in the cache +func (slru *slru) Len() int { + return slru.probationLs.Len() + slru.protectedLs.Len() +} + +// Remove removes an item from the cache, returning the item and a boolean indicating if it was found +func (slru *slru) Remove(key interface{}) (interface{}, bool) { + v, ok := slru.data[key] + if !ok { + return nil, false + } + + item := v.Value.(*slruItem) + + if item.listId == protectedSegment { + slru.protectedLs.Remove(v) + } else { + slru.probationLs.Remove(v) + } + + delete(slru.data, key) + + return item.value, true +} + +func (slru *slru) clear() { + slru.probationLs.Init() + slru.protectedLs.Init() +} + +// lru is an LRU cache. +type lru struct { + data map[interface{}]*list.Element + cap int + evictList *list.List +} + +func newLRU(cap int, data map[interface{}]*list.Element) *lru { + return &lru{ + data: data, + cap: cap, + evictList: list.New(), + } +} + +// Get returns a value from the cache +func (lru *lru) get(v *list.Element) { + lru.evictList.MoveToFront(v) +} + +// Set a value in the cache +func (lru *lru) add(newItem slruItem) (oldItem slruItem, evicted bool) { + if lru.evictList.Len() < lru.cap { + lru.data[newItem.key] = lru.evictList.PushFront(&newItem) + return slruItem{}, false + } + + // reuse the tail item + e := lru.evictList.Back() + item := e.Value.(*slruItem) + + delete(lru.data, item.key) + + oldItem = *item + *item = newItem + + lru.data[item.key] = e + lru.evictList.MoveToFront(e) + + return oldItem, true +} + +// Len returns the number of items in the cache. +func (lru *lru) Len() int { + return lru.evictList.Len() +} + +// Remove removes the provided key from the cache +func (lru *lru) Remove(key interface{}) (interface{}, bool) { + v, ok := lru.data[key] + if !ok { + return nil, false + } + item := v.Value.(*slruItem) + lru.evictList.Remove(v) + delete(lru.data, key) + return item.value, true +} + +func (lru *lru) clear() { + lru.evictList.Init() +} diff --git a/core/hotspot/cache/tinylfu.go b/core/hotspot/cache/tinylfu.go new file mode 100644 index 000000000..bc5c971f8 --- /dev/null +++ b/core/hotspot/cache/tinylfu.go @@ -0,0 +1,185 @@ +package cache + +import ( + "container/list" + "errors" +) + +const ( + samplesFactor = 8 + doorkeeperFactor = 8 + countersFactor = 1 + falsePositiveProbability = 0.1 + lruRatio = 0.01 +) + +// TinyLfu is an implementation of the TinyLfu algorithm: https://arxiv.org/pdf/1512.00727.pdf +// Window Cache Victim .---------. Main Cache Victim +// .------------------->| TinyLFU |<-----------------. +// | `---------' | +// .-------------------. | .------------------. +// | Window Cache (1%) | | | Main Cache (99%) | +// | (LRU) | | | (SLRU) | +// `-------------------' | `------------------' +// ^ | ^ +// | `---------------' +// new item Winner +type TinyLfu struct { + countMinSketch *countMinSketch + doorkeeper *doorkeeper + additions int + samples int + lru *lru + slru *slru + items map[interface{}]*list.Element +} + +func NewTinyLfu(cap int) (*TinyLfu, error) { + if cap <= 0 { + return nil, errors.New("Must provide a positive size") + } + if cap < 100 { + cap = 100 + } + lruCap := int(float64(cap) * lruRatio) + slruSize := cap - lruCap + + items := make(map[interface{}]*list.Element) + return &TinyLfu{ + countMinSketch: newCountMinSketch(countersFactor * cap), + additions: 0, + samples: samplesFactor * cap, + doorkeeper: newDoorkeeper(doorkeeperFactor*cap, falsePositiveProbability), + items: items, + lru: newLRU(lruCap, items), + slru: newSLRU(slruSize, items), + }, nil +} + +// Get looks up a key's value from the cache. +func (t *TinyLfu) Get(key interface{}) (interface{}, bool) { + t.additions++ + if t.additions == t.samples { + t.countMinSketch.reset() + t.doorkeeper.reset() + t.additions = 0 + } + + val, ok := t.items[key] + if !ok { + keyHash := sum(key) + if t.doorkeeper.put(keyHash) { + t.countMinSketch.add(keyHash) + } + return nil, false + } + + item := val.Value.(*slruItem) + if t.doorkeeper.put(item.keyHash) { + t.countMinSketch.add(item.keyHash) + } + + v := item.value + if item.listId == admissionWindow { + t.lru.get(val) + } else { + t.slru.get(val) + } + + return v, true +} + +// Contains checks if a key is in the cache without updating +func (t *TinyLfu) Contains(key interface{}) (ok bool) { + _, ok = t.items[key] + return ok +} + +func (t *TinyLfu) Add(key interface{}, val interface{}) { + t.AddIfAbsent(key, val) +} + +// AddIfAbsent adds item only if key is not existed. +func (t *TinyLfu) AddIfAbsent(key interface{}, val interface{}) (priorValue interface{}) { + + // Check for existing item + if v, ok := t.Get(key); ok { + return v + } + + newItem := slruItem{admissionWindow, key, val, sum(key)} + + candidate, evicted := t.lru.add(newItem) + if !evicted { + return nil + } + + // Estimate count of what will be evicted from slru + victim := t.slru.victim() + if victim == nil { + t.slru.add(candidate) + return nil + } + + victimCount := t.estimate(victim.keyHash) + candidateCount := t.estimate(candidate.keyHash) + + if candidateCount > victimCount { + t.slru.add(candidate) + } + return nil +} + +// estimate estimates frequency of the given hash value. +func (t *TinyLfu) estimate(h uint64) uint8 { + freq := t.countMinSketch.estimate(h) + if t.doorkeeper.contains(h) { + freq++ + } + return freq +} + +func (t *TinyLfu) Remove(key interface{}) (isFound bool) { + // Check for existing item + val, ok := t.items[key] + if !ok { + return false + } + + item := val.Value.(*slruItem) + if item.listId == admissionWindow { + t.lru.Remove(key) + return true + } else { + t.slru.Remove(key) + return true + } +} + +// Keys returns a slice of the keys in the cache, from oldest to newest. +func (t *TinyLfu) Keys() []interface{} { + i := 0 + keys := make([]interface{}, len(t.items)) + for k := range t.items { + keys[i] = k + i++ + } + return keys +} + +// Len returns the number of items in the cache. +func (t *TinyLfu) Len() int { + return len(t.items) +} + +// Purge is used to completely clear the cache. +func (t *TinyLfu) Purge() { + for k, _ := range t.items { + delete(t.items, k) + } + t.slru.clear() + t.additions = 0 + t.lru.clear() + t.doorkeeper.reset() + t.countMinSketch.clear() +} diff --git a/core/hotspot/cache/tinylfu_test.go b/core/hotspot/cache/tinylfu_test.go new file mode 100644 index 000000000..f0ad0234a --- /dev/null +++ b/core/hotspot/cache/tinylfu_test.go @@ -0,0 +1,86 @@ +package cache + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +type tinyLFUTest struct { + lfu *TinyLfu + t *testing.T +} + +func (t *tinyLFUTest) assertCap(n int) { + assert.True(t.t, t.lfu.lru.cap+t.lfu.slru.protectedCap+t.lfu.slru.probationCap == n) +} + +func (t *tinyLFUTest) assertLen(admission, protected, probation int) { + sz := t.lfu.Len() + tz := t.lfu.slru.protectedLs.Len() + bz := t.lfu.slru.probationLs.Len() + assert.True(t.t, sz == admission+protected+probation && tz == protected && bz == probation) +} + +func (t *tinyLFUTest) assertLRUValue(k int, id uint8) { + v := t.lfu.items[k].Value.(*slruItem).value + assert.True(t.t, v != nil) + ak := k + av := v + listId := t.lfu.items[k].Value.(*slruItem).listId + assert.True(t.t, ak == av && listId == id) +} + +func TestTinyLFU(t *testing.T) { + t.Run("Test_TinyLFU", func(t *testing.T) { + + s := tinyLFUTest{t: t} + s.lfu, _ = NewTinyLfu(200) + s.assertCap(200) + s.lfu.slru.protectedCap = 2 + s.lfu.slru.probationCap = 1 + for i := 0; i < 5; i++ { + e := s.lfu.AddIfAbsent(i, i) + assert.True(t, e == nil) + } + // 4 3 | - | 2 1 0 + s.assertLen(2, 0, 3) + s.assertLRUValue(4, admissionWindow) + s.assertLRUValue(3, admissionWindow) + s.assertLRUValue(2, probationSegment) + s.assertLRUValue(1, probationSegment) + s.assertLRUValue(0, probationSegment) + + s.lfu.Get(1) + s.lfu.Get(2) + // 4 3 | 2 1 | 0 + s.assertLen(2, 2, 1) + s.assertLRUValue(2, protectedSegment) + s.assertLRUValue(1, protectedSegment) + s.assertLRUValue(0, probationSegment) + + s.lfu.AddIfAbsent(5, 5) + // 5 4 | 2 1 | 0 + s.assertLRUValue(5, admissionWindow) + s.assertLRUValue(4, admissionWindow) + s.assertLRUValue(2, protectedSegment) + s.assertLRUValue(1, protectedSegment) + s.assertLRUValue(0, probationSegment) + + s.lfu.Get(4) + s.lfu.Get(5) + s.lfu.AddIfAbsent(6, 6) + // 6 5 | 2 1 | 4 + s.assertLRUValue(6, admissionWindow) + s.assertLRUValue(5, admissionWindow) + s.assertLRUValue(2, protectedSegment) + s.assertLRUValue(1, protectedSegment) + s.assertLRUValue(4, probationSegment) + s.assertLen(2, 2, 1) + n := s.lfu.estimate(sum(1)) + assert.True(t, n == 2) + s.lfu.Get(2) + s.lfu.Get(2) + n = s.lfu.estimate(sum(2)) + assert.True(t, n == 4) + }) +} diff --git a/core/hotspot/rule_manager_test.go b/core/hotspot/rule_manager_test.go index f53916c14..af31ee5cb 100644 --- a/core/hotspot/rule_manager_test.go +++ b/core/hotspot/rule_manager_test.go @@ -69,9 +69,9 @@ func Test_tcGenFuncMap(t *testing.T) { size = ParamsMaxCapacity } metric := &ParamsMetric{ - RuleTimeCounter: cache.NewLRUCacheMap(size), - RuleTokenCounter: cache.NewLRUCacheMap(size), - ConcurrencyCounter: cache.NewLRUCacheMap(ConcurrencyMaxCount), + RuleTimeCounter: cache.NewTinyLfuCacheMap(size), + RuleTokenCounter: cache.NewTinyLfuCacheMap(size), + ConcurrencyCounter: cache.NewTinyLfuCacheMap(ConcurrencyMaxCount), } tc := generator(r1, metric) diff --git a/core/hotspot/traffic_shaping.go b/core/hotspot/traffic_shaping.go index 2832913aa..34f43bada 100644 --- a/core/hotspot/traffic_shaping.go +++ b/core/hotspot/traffic_shaping.go @@ -64,9 +64,9 @@ func newBaseTrafficShapingController(r *Rule) *baseTrafficShapingController { size = ParamsMaxCapacity } metric := &ParamsMetric{ - RuleTimeCounter: cache.NewLRUCacheMap(size), - RuleTokenCounter: cache.NewLRUCacheMap(size), - ConcurrencyCounter: cache.NewLRUCacheMap(ConcurrencyMaxCount), + RuleTimeCounter: cache.NewTinyLfuCacheMap(size), + RuleTokenCounter: cache.NewTinyLfuCacheMap(size), + ConcurrencyCounter: cache.NewTinyLfuCacheMap(ConcurrencyMaxCount), } return newBaseTrafficShapingControllerWithMetric(r, metric) } From 285288fb088a7c601d9f2d864c6f7ac83651b2cc Mon Sep 17 00:00:00 2001 From: liqiangz Date: Tue, 13 Oct 2020 17:27:45 +0800 Subject: [PATCH 02/24] fix ci --- core/hotspot/cache/tinylfu.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hotspot/cache/tinylfu.go b/core/hotspot/cache/tinylfu.go index bc5c971f8..3f58741b0 100644 --- a/core/hotspot/cache/tinylfu.go +++ b/core/hotspot/cache/tinylfu.go @@ -174,7 +174,7 @@ func (t *TinyLfu) Len() int { // Purge is used to completely clear the cache. func (t *TinyLfu) Purge() { - for k, _ := range t.items { + for k := range t.items { delete(t.items, k) } t.slru.clear() From 33334e99377865ca9041483211236e49c2248280 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Wed, 14 Oct 2020 10:10:45 +0800 Subject: [PATCH 03/24] fix ci --- core/hotspot/cache/doorkeeper.go | 2 +- core/hotspot/cache/hash.go | 1 - core/hotspot/cache/tinylfu_test.go | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/hotspot/cache/doorkeeper.go b/core/hotspot/cache/doorkeeper.go index e3e76aa88..ae30bc75e 100644 --- a/core/hotspot/cache/doorkeeper.go +++ b/core/hotspot/cache/doorkeeper.go @@ -38,7 +38,7 @@ func newDoorkeeper(ins int, falsePositiveRate float64) *doorkeeper { // put inserts a hash value into the bloom filter. // returns true if the value may already in the doorkeeper. func (d *doorkeeper) put(h uint64) bool { - //only protectedLs hash functions are necessary to effectively + //only two hash functions are necessary to effectively //implement a Bloom filter without any loss in the asymptotic false positive probability //split up 64-bit hashcode into protectedLs 32-bit hashcode hash1, hash2 := uint32(h), uint32(h>>32) diff --git a/core/hotspot/cache/hash.go b/core/hotspot/cache/hash.go index e129bca54..aae613e38 100644 --- a/core/hotspot/cache/hash.go +++ b/core/hotspot/cache/hash.go @@ -52,7 +52,6 @@ func sum(k interface{}) uint64 { return h } return 0 - } func hashU64(data uint64) uint64 { diff --git a/core/hotspot/cache/tinylfu_test.go b/core/hotspot/cache/tinylfu_test.go index f0ad0234a..e808d6ac3 100644 --- a/core/hotspot/cache/tinylfu_test.go +++ b/core/hotspot/cache/tinylfu_test.go @@ -1,8 +1,9 @@ package cache import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) type tinyLFUTest struct { From 5fd1210d370455959f695e991d5ba18b2046476c Mon Sep 17 00:00:00 2001 From: liqiangz Date: Sat, 21 Nov 2020 15:59:20 +0800 Subject: [PATCH 04/24] fix --- core/hotspot/traffic_shaping.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/hotspot/traffic_shaping.go b/core/hotspot/traffic_shaping.go index fd4a5f656..80355fa76 100644 --- a/core/hotspot/traffic_shaping.go +++ b/core/hotspot/traffic_shaping.go @@ -69,8 +69,8 @@ func newBaseTrafficShapingController(r *Rule) *baseTrafficShapingController { size = ParamsMaxCapacity } metric := &ParamsMetric{ - RuleTimeCounter: cache.NewLRUCacheMap(size), - RuleTokenCounter: cache.NewLRUCacheMap(size), + RuleTimeCounter: cache.NewTinyLfuCacheMap(size), + RuleTokenCounter: cache.NewTinyLfuCacheMap(size), } return newBaseTrafficShapingControllerWithMetric(r, metric) case Concurrency: @@ -81,7 +81,7 @@ func newBaseTrafficShapingController(r *Rule) *baseTrafficShapingController { size = ConcurrencyMaxCount } metric := &ParamsMetric{ - ConcurrencyCounter: cache.NewLRUCacheMap(size), + ConcurrencyCounter: cache.NewTinyLfuCacheMap(size), } return newBaseTrafficShapingControllerWithMetric(r, metric) default: From fc714f3628b6f5f32dbb0e1eaf65f3978aa95a3d Mon Sep 17 00:00:00 2001 From: liqiangz Date: Sat, 21 Nov 2020 16:51:49 +0800 Subject: [PATCH 05/24] Add more test case --- core/hotspot/cache/count_min_sketch_test.go | 30 +++++++++++++++++++++ core/hotspot/cache/doorkeeper_test.go | 22 +++++++++++++++ core/hotspot/cache/slru.go | 14 +++++----- 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 core/hotspot/cache/count_min_sketch_test.go create mode 100644 core/hotspot/cache/doorkeeper_test.go diff --git a/core/hotspot/cache/count_min_sketch_test.go b/core/hotspot/cache/count_min_sketch_test.go new file mode 100644 index 000000000..b44b5edec --- /dev/null +++ b/core/hotspot/cache/count_min_sketch_test.go @@ -0,0 +1,30 @@ +package cache + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCountMinSketch(t *testing.T) { + t.Run("Test_CountMinSketch", func(t *testing.T) { + max := 15 + cm4 := newCountMinSketch(max) + for i := 0; i < max; i++ { + for j := i; j > 0; j-- { + cm4.add(uint64(i)) + } + assert.True(t, uint64(i) == uint64(cm4.estimate(uint64(i)))) + } + + cm4.reset() + for i := 0; i < max; i++ { + assert.True(t, uint64(i)/2 == uint64(cm4.estimate(uint64(i)))) + } + + cm4.clear() + for i := 0; i < max; i++ { + assert.True(t, 0 == uint64(cm4.estimate(uint64(i)))) + } + }) +} diff --git a/core/hotspot/cache/doorkeeper_test.go b/core/hotspot/cache/doorkeeper_test.go new file mode 100644 index 000000000..cc33dba72 --- /dev/null +++ b/core/hotspot/cache/doorkeeper_test.go @@ -0,0 +1,22 @@ +package cache + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDoorkeeper(t *testing.T) { + t.Run("Test_Doorkeeper", func(t *testing.T) { + max := 1500 + filter := newDoorkeeper(1500, 0.001) + for i := 0; i < max; i++ { + filter.put(uint64(i)) + assert.True(t, true == filter.contains(uint64(i))) + } + filter.reset() + for i := 0; i < max; i++ { + assert.True(t, false == filter.contains(uint64(i))) + } + }) +} diff --git a/core/hotspot/cache/slru.go b/core/hotspot/cache/slru.go index 6e99228db..ccf88a4ec 100644 --- a/core/hotspot/cache/slru.go +++ b/core/hotspot/cache/slru.go @@ -55,7 +55,7 @@ func (slru *slru) get(v *list.Element) { back := slru.protectedLs.Back() backItem := back.Value.(*slruItem) - // swap the item + // swap the two item *backItem, *item = *item, *backItem backItem.listId = protectedSegment item.listId = probationSegment @@ -72,18 +72,18 @@ func (slru *slru) add(newItem slruItem) { newItem.listId = probationSegment - if slru.probationLs.Len() < slru.probationCap || (slru.Len() < slru.probationCap+slru.protectedCap) { + if slru.probationLs.Len() < slru.probationCap || slru.Len() < slru.probationCap+slru.protectedCap { slru.data[newItem.key] = slru.probationLs.PushFront(&newItem) return } - e := slru.probationLs.Back() - item := e.Value.(*slruItem) + back := slru.probationLs.Back() + item := back.Value.(*slruItem) delete(slru.data, item.key) *item = newItem - slru.data[item.key] = e - slru.probationLs.MoveToFront(e) + slru.data[item.key] = back + slru.probationLs.MoveToFront(back) } func (slru *slru) victim() *slruItem { @@ -154,7 +154,7 @@ func (lru *lru) add(newItem slruItem) (oldItem slruItem, evicted bool) { return slruItem{}, false } - // reuse the tail item + // reuse the item e := lru.evictList.Back() item := e.Value.(*slruItem) From 739d121588b43b58548a71ee6f971cd7e9218d92 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Sun, 22 Nov 2020 14:52:28 +0800 Subject: [PATCH 06/24] Add hitrate_test --- core/hotspot/cache/hitrate_test.go | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 core/hotspot/cache/hitrate_test.go diff --git a/core/hotspot/cache/hitrate_test.go b/core/hotspot/cache/hitrate_test.go new file mode 100644 index 000000000..8944e84d1 --- /dev/null +++ b/core/hotspot/cache/hitrate_test.go @@ -0,0 +1,57 @@ +package cache + +import ( + "fmt" + "math/rand" + "testing" +) + +func testBySize(cacheSize int, zipf *rand.Zipf) { + lfu, _ := NewTinyLfu(cacheSize) + lru, _ := NewLRU(cacheSize, nil) + totalLfu := 0 + missLfu := 0 + for i := 0; i < 2000000; i++ { + totalLfu++ + key := zipf.Uint64() + _, ok := lfu.Get(key) + if !ok { + missLfu++ + lfu.Add(key, key) + } + } + + fmt.Printf("tinyLfu cache size %d, total %d, miss %d, hitRate %f \n", cacheSize, totalLfu, missLfu, (float64(totalLfu)-float64(missLfu))/float64(totalLfu)) + + totalLru := 0 + missLru := 0 + for i := 0; i < 1000000; i++ { + totalLru++ + key := zipf.Uint64() + _, ok := lru.Get(key) + if !ok { + missLru++ + lru.Add(key, key) + } + } + fmt.Printf("lru cache size %d, total %d, miss %d, hitRate %f \n \n", cacheSize, totalLru, missLru, (float64(totalLru)-float64(missLru))/float64(totalLru)) +} + +func TestHitRate(t *testing.T) { + t.Run("Test_HitRate", func(t *testing.T) { + r := rand.New(rand.NewSource(1)) + zipf := rand.NewZipf( + r, + 1.01, + 1.0, + 1<<18-1, + ) + testBySize(100, zipf) + testBySize(500, zipf) + testBySize(1000, zipf) + testBySize(5000, zipf) + testBySize(10000, zipf) + testBySize(20000, zipf) + testBySize(50000, zipf) + }) +} From 4389d1ca07ee28d115fc0d2f52248755c1878a58 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Sun, 22 Nov 2020 14:53:44 +0800 Subject: [PATCH 07/24] Add hitrate_test --- core/hotspot/cache/hitrate_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hotspot/cache/hitrate_test.go b/core/hotspot/cache/hitrate_test.go index 8944e84d1..64fdbc3dc 100644 --- a/core/hotspot/cache/hitrate_test.go +++ b/core/hotspot/cache/hitrate_test.go @@ -25,7 +25,7 @@ func testBySize(cacheSize int, zipf *rand.Zipf) { totalLru := 0 missLru := 0 - for i := 0; i < 1000000; i++ { + for i := 0; i < 2000000; i++ { totalLru++ key := zipf.Uint64() _, ok := lru.Get(key) From e7ec61486f877ed6eed66b894a7d3e788a24317a Mon Sep 17 00:00:00 2001 From: liqiangz Date: Wed, 25 Nov 2020 16:28:53 +0800 Subject: [PATCH 08/24] refine --- core/hotspot/cache/slru.go | 14 -------------- core/hotspot/cache/tinylfu.go | 3 --- 2 files changed, 17 deletions(-) diff --git a/core/hotspot/cache/slru.go b/core/hotspot/cache/slru.go index ccf88a4ec..bc989ef08 100644 --- a/core/hotspot/cache/slru.go +++ b/core/hotspot/cache/slru.go @@ -39,19 +39,16 @@ func newSLRU(cap int, data map[interface{}]*list.Element) *slru { // Get looks up a key's value from the cache. func (slru *slru) get(v *list.Element) { item := v.Value.(*slruItem) - if item.listId == protectedSegment { slru.protectedLs.MoveToFront(v) return } - if slru.protectedLs.Len() < slru.protectedCap { slru.probationLs.Remove(v) item.listId = protectedSegment slru.data[item.key] = slru.protectedLs.PushFront(item) return } - back := slru.protectedLs.Back() backItem := back.Value.(*slruItem) @@ -69,31 +66,24 @@ func (slru *slru) get(v *list.Element) { // add set a value in the cache func (slru *slru) add(newItem slruItem) { - newItem.listId = probationSegment - if slru.probationLs.Len() < slru.probationCap || slru.Len() < slru.probationCap+slru.protectedCap { slru.data[newItem.key] = slru.probationLs.PushFront(&newItem) return } - back := slru.probationLs.Back() item := back.Value.(*slruItem) delete(slru.data, item.key) *item = newItem - slru.data[item.key] = back slru.probationLs.MoveToFront(back) } func (slru *slru) victim() *slruItem { - if slru.Len() < slru.probationCap+slru.protectedCap { return nil } - v := slru.probationLs.Back() - return v.Value.(*slruItem) } @@ -108,17 +98,13 @@ func (slru *slru) Remove(key interface{}) (interface{}, bool) { if !ok { return nil, false } - item := v.Value.(*slruItem) - if item.listId == protectedSegment { slru.protectedLs.Remove(v) } else { slru.probationLs.Remove(v) } - delete(slru.data, key) - return item.value, true } diff --git a/core/hotspot/cache/tinylfu.go b/core/hotspot/cache/tinylfu.go index 3f58741b0..4205ad7e7 100644 --- a/core/hotspot/cache/tinylfu.go +++ b/core/hotspot/cache/tinylfu.go @@ -73,7 +73,6 @@ func (t *TinyLfu) Get(key interface{}) (interface{}, bool) { } return nil, false } - item := val.Value.(*slruItem) if t.doorkeeper.put(item.keyHash) { t.countMinSketch.add(item.keyHash) @@ -108,7 +107,6 @@ func (t *TinyLfu) AddIfAbsent(key interface{}, val interface{}) (priorValue inte } newItem := slruItem{admissionWindow, key, val, sum(key)} - candidate, evicted := t.lru.add(newItem) if !evicted { return nil @@ -123,7 +121,6 @@ func (t *TinyLfu) AddIfAbsent(key interface{}, val interface{}) (priorValue inte victimCount := t.estimate(victim.keyHash) candidateCount := t.estimate(candidate.keyHash) - if candidateCount > victimCount { t.slru.add(candidate) } From 7a87c15102ca44bfcc72f68ea4f944e185662488 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Wed, 25 Nov 2020 16:29:46 +0800 Subject: [PATCH 09/24] refine --- core/hotspot/cache/slru.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/hotspot/cache/slru.go b/core/hotspot/cache/slru.go index bc989ef08..3c94f2c6b 100644 --- a/core/hotspot/cache/slru.go +++ b/core/hotspot/cache/slru.go @@ -143,15 +143,11 @@ func (lru *lru) add(newItem slruItem) (oldItem slruItem, evicted bool) { // reuse the item e := lru.evictList.Back() item := e.Value.(*slruItem) - delete(lru.data, item.key) - oldItem = *item *item = newItem - lru.data[item.key] = e lru.evictList.MoveToFront(e) - return oldItem, true } From ee625948e6e1ef9d98720082b5e194a6ed6dc617 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Mon, 14 Dec 2020 21:03:09 +0800 Subject: [PATCH 10/24] improve hash and create package --- .../hotspot/cache/{ => lru}/concurrent_lru.go | 6 ++- .../concurrent_lru_benchmark_test.go | 2 +- .../cache/{ => lru}/concurrent_lru_test.go | 2 +- core/hotspot/cache/{ => lru}/lru.go | 2 +- .../{ => wtinylfu}/concurrent_tinylfu.go | 6 ++- .../concurrent_tinylfu_benchmark_test.go | 2 +- .../cache/{ => wtinylfu}/count_min_sketch.go | 6 ++- .../{ => wtinylfu}/count_min_sketch_test.go | 2 +- .../cache/{ => wtinylfu}/doorkeeper.go | 4 +- .../cache/{ => wtinylfu}/doorkeeper_test.go | 2 +- core/hotspot/cache/{ => wtinylfu}/hash.go | 29 ++++++------- .../cache/wtinylfu/hash_benchmark_test.go | 43 +++++++++++++++++++ .../cache/{ => wtinylfu}/hitrate_test.go | 6 ++- core/hotspot/cache/{ => wtinylfu}/slru.go | 2 +- core/hotspot/cache/{ => wtinylfu}/tinylfu.go | 2 +- .../cache/{ => wtinylfu}/tinylfu_test.go | 2 +- core/hotspot/rule_manager_test.go | 8 ++-- core/hotspot/traffic_shaping.go | 8 ++-- 18 files changed, 92 insertions(+), 42 deletions(-) rename core/hotspot/cache/{ => lru}/concurrent_lru.go (91%) rename core/hotspot/cache/{ => lru}/concurrent_lru_benchmark_test.go (98%) rename core/hotspot/cache/{ => lru}/concurrent_lru_test.go (99%) rename core/hotspot/cache/{ => lru}/lru.go (99%) rename core/hotspot/cache/{ => wtinylfu}/concurrent_tinylfu.go (91%) rename core/hotspot/cache/{ => wtinylfu}/concurrent_tinylfu_benchmark_test.go (98%) rename core/hotspot/cache/{ => wtinylfu}/count_min_sketch.go (95%) rename core/hotspot/cache/{ => wtinylfu}/count_min_sketch_test.go (97%) rename core/hotspot/cache/{ => wtinylfu}/doorkeeper.go (96%) rename core/hotspot/cache/{ => wtinylfu}/doorkeeper_test.go (96%) rename core/hotspot/cache/{ => wtinylfu}/hash.go (78%) create mode 100644 core/hotspot/cache/wtinylfu/hash_benchmark_test.go rename core/hotspot/cache/{ => wtinylfu}/hitrate_test.go (90%) rename core/hotspot/cache/{ => wtinylfu}/slru.go (99%) rename core/hotspot/cache/{ => wtinylfu}/tinylfu.go (99%) rename core/hotspot/cache/{ => wtinylfu}/tinylfu_test.go (99%) diff --git a/core/hotspot/cache/concurrent_lru.go b/core/hotspot/cache/lru/concurrent_lru.go similarity index 91% rename from core/hotspot/cache/concurrent_lru.go rename to core/hotspot/cache/lru/concurrent_lru.go index e9239ec48..aa891f1ea 100644 --- a/core/hotspot/cache/concurrent_lru.go +++ b/core/hotspot/cache/lru/concurrent_lru.go @@ -1,7 +1,9 @@ -package cache +package lru import ( "sync" + + "github.com/alibaba/sentinel-golang/core/hotspot/cache" ) // LruCacheMap use LRU strategy to cache the most frequently accessed hotspot parameter @@ -76,7 +78,7 @@ func (c *LruCacheMap) Purge() { c.lru.Purge() } -func NewLRUCacheMap(size int) ConcurrentCounterCache { +func NewLRUCacheMap(size int) cache.ConcurrentCounterCache { lru, err := NewLRU(size, nil) if err != nil { return nil diff --git a/core/hotspot/cache/concurrent_lru_benchmark_test.go b/core/hotspot/cache/lru/concurrent_lru_benchmark_test.go similarity index 98% rename from core/hotspot/cache/concurrent_lru_benchmark_test.go rename to core/hotspot/cache/lru/concurrent_lru_benchmark_test.go index cde5060b4..9951cbe86 100644 --- a/core/hotspot/cache/concurrent_lru_benchmark_test.go +++ b/core/hotspot/cache/lru/concurrent_lru_benchmark_test.go @@ -1,4 +1,4 @@ -package cache +package lru import ( "strconv" diff --git a/core/hotspot/cache/concurrent_lru_test.go b/core/hotspot/cache/lru/concurrent_lru_test.go similarity index 99% rename from core/hotspot/cache/concurrent_lru_test.go rename to core/hotspot/cache/lru/concurrent_lru_test.go index 42d079a8d..c0d935813 100644 --- a/core/hotspot/cache/concurrent_lru_test.go +++ b/core/hotspot/cache/lru/concurrent_lru_test.go @@ -1,4 +1,4 @@ -package cache +package lru import ( "strconv" diff --git a/core/hotspot/cache/lru.go b/core/hotspot/cache/lru/lru.go similarity index 99% rename from core/hotspot/cache/lru.go rename to core/hotspot/cache/lru/lru.go index c5cac7ddb..fc2dbc230 100644 --- a/core/hotspot/cache/lru.go +++ b/core/hotspot/cache/lru/lru.go @@ -1,4 +1,4 @@ -package cache +package lru import ( "container/list" diff --git a/core/hotspot/cache/concurrent_tinylfu.go b/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go similarity index 91% rename from core/hotspot/cache/concurrent_tinylfu.go rename to core/hotspot/cache/wtinylfu/concurrent_tinylfu.go index 70fe371c1..c8204318a 100644 --- a/core/hotspot/cache/concurrent_tinylfu.go +++ b/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go @@ -1,7 +1,9 @@ -package cache +package wtinylfu import ( "sync" + + "github.com/alibaba/sentinel-golang/core/hotspot/cache" ) // TinyLfuCacheMap use tinyLfu strategy to cache the most frequently accessed hotspot parameter @@ -76,7 +78,7 @@ func (c *TinyLfuCacheMap) Purge() { c.tinyLfu.Purge() } -func NewTinyLfuCacheMap(size int) ConcurrentCounterCache { +func NewTinyLfuCacheMap(size int) cache.ConcurrentCounterCache { tinyLfu, err := NewTinyLfu(size) if err != nil { return nil diff --git a/core/hotspot/cache/concurrent_tinylfu_benchmark_test.go b/core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go similarity index 98% rename from core/hotspot/cache/concurrent_tinylfu_benchmark_test.go rename to core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go index 9ba6dcc1b..0b44f77a9 100644 --- a/core/hotspot/cache/concurrent_tinylfu_benchmark_test.go +++ b/core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go @@ -1,4 +1,4 @@ -package cache +package wtinylfu import ( "strconv" diff --git a/core/hotspot/cache/count_min_sketch.go b/core/hotspot/cache/wtinylfu/count_min_sketch.go similarity index 95% rename from core/hotspot/cache/count_min_sketch.go rename to core/hotspot/cache/wtinylfu/count_min_sketch.go index 5cb41b02a..879245fd6 100644 --- a/core/hotspot/cache/count_min_sketch.go +++ b/core/hotspot/cache/wtinylfu/count_min_sketch.go @@ -1,7 +1,9 @@ -package cache +package wtinylfu const sketchDepth = 4 +const resetMask = 0x7777777777777777 + // countMinSketch is an implementation of count-min sketch with 4-bit counters. type countMinSketch struct { counters []uint64 @@ -52,7 +54,7 @@ func (c *countMinSketch) reset() { for i, v := range c.counters { if v != 0 { //divides all by two. - c.counters[i] = (v >> 1) & 0x7777777777777777 + c.counters[i] = (v >> 1) & resetMask } } } diff --git a/core/hotspot/cache/count_min_sketch_test.go b/core/hotspot/cache/wtinylfu/count_min_sketch_test.go similarity index 97% rename from core/hotspot/cache/count_min_sketch_test.go rename to core/hotspot/cache/wtinylfu/count_min_sketch_test.go index b44b5edec..527b9f913 100644 --- a/core/hotspot/cache/count_min_sketch_test.go +++ b/core/hotspot/cache/wtinylfu/count_min_sketch_test.go @@ -1,4 +1,4 @@ -package cache +package wtinylfu import ( "testing" diff --git a/core/hotspot/cache/doorkeeper.go b/core/hotspot/cache/wtinylfu/doorkeeper.go similarity index 96% rename from core/hotspot/cache/doorkeeper.go rename to core/hotspot/cache/wtinylfu/doorkeeper.go index ae30bc75e..fbc86e1f9 100644 --- a/core/hotspot/cache/doorkeeper.go +++ b/core/hotspot/cache/wtinylfu/doorkeeper.go @@ -1,4 +1,4 @@ -package cache +package wtinylfu import "math" @@ -84,7 +84,7 @@ func (d *doorkeeper) reset() { } } -// return the integer >= i which is a power of protectedLs +// return the integer >= i which is a power of two func nextPowerOfTwo(i uint32) uint32 { n := i - 1 n |= n >> 1 diff --git a/core/hotspot/cache/doorkeeper_test.go b/core/hotspot/cache/wtinylfu/doorkeeper_test.go similarity index 96% rename from core/hotspot/cache/doorkeeper_test.go rename to core/hotspot/cache/wtinylfu/doorkeeper_test.go index cc33dba72..be803db4f 100644 --- a/core/hotspot/cache/doorkeeper_test.go +++ b/core/hotspot/cache/wtinylfu/doorkeeper_test.go @@ -1,4 +1,4 @@ -package cache +package wtinylfu import ( "testing" diff --git a/core/hotspot/cache/hash.go b/core/hotspot/cache/wtinylfu/hash.go similarity index 78% rename from core/hotspot/cache/hash.go rename to core/hotspot/cache/wtinylfu/hash.go index aae613e38..fc9de38c8 100644 --- a/core/hotspot/cache/hash.go +++ b/core/hotspot/cache/wtinylfu/hash.go @@ -1,14 +1,17 @@ -package cache +package wtinylfu import ( - "bytes" "encoding/binary" - "encoding/gob" + "fmt" "hash/fnv" "math" "reflect" ) +var ( + fnv64 = fnv.New64() +) + func sum(k interface{}) uint64 { switch h := k.(type) { case int: @@ -48,7 +51,7 @@ func sum(k interface{}) uint64 { if h, ok := hashPointer(k); ok { return h } - if h, ok := hashWithGob(k); ok { + if h, ok := hashWithSprintf(k); ok { return h } return 0 @@ -64,23 +67,19 @@ func hashString(data string) uint64 { return hashByteArray([]byte(data)) } -func hashWithGob(data interface{}) (uint64, bool) { - var buf bytes.Buffer - enc := gob.NewEncoder(&buf) - err := enc.Encode(data) - if err != nil { - return 0, false - } - return hashByteArray(buf.Bytes()), true +func hashWithSprintf(data interface{}) (uint64, bool) { + v := fmt.Sprintf("%v", data) + return hashString(v), true } func hashByteArray(bytes []byte) uint64 { - f := fnv.New64() - _, err := f.Write(bytes) + _, err := fnv64.Write(bytes) if err != nil { return 0 } - return binary.LittleEndian.Uint64(f.Sum(nil)) + hash := binary.LittleEndian.Uint64(fnv64.Sum(nil)) + fnv64.Reset() + return hash } func hashPointer(k interface{}) (uint64, bool) { diff --git a/core/hotspot/cache/wtinylfu/hash_benchmark_test.go b/core/hotspot/cache/wtinylfu/hash_benchmark_test.go new file mode 100644 index 000000000..2132469b0 --- /dev/null +++ b/core/hotspot/cache/wtinylfu/hash_benchmark_test.go @@ -0,0 +1,43 @@ +package wtinylfu + +import ( + "testing" +) + +func Benchmark_Hash_Num(b *testing.B) { + num := 100020000 + b.ResetTimer() + for i := 0; i < b.N; i++ { + sum(num) + } +} +func Benchmark_Hash_String(b *testing.B) { + str := "test" + b.ResetTimer() + for i := 0; i < b.N; i++ { + sum(str) + } +} + +func Benchmark_Hash_Pointer(b *testing.B) { + num := 100020000 + pointer := &num + b.ResetTimer() + for i := 0; i < b.N; i++ { + sum(pointer) + } +} +func Benchmark_Hash_WithSprintf(b *testing.B) { + type test struct { + test1 uint32 + test2 string + } + s := test{ + 1, + "test2222", + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + sum(s) + } +} diff --git a/core/hotspot/cache/hitrate_test.go b/core/hotspot/cache/wtinylfu/hitrate_test.go similarity index 90% rename from core/hotspot/cache/hitrate_test.go rename to core/hotspot/cache/wtinylfu/hitrate_test.go index 64fdbc3dc..edebf7cb6 100644 --- a/core/hotspot/cache/hitrate_test.go +++ b/core/hotspot/cache/wtinylfu/hitrate_test.go @@ -1,14 +1,16 @@ -package cache +package wtinylfu import ( "fmt" "math/rand" "testing" + + lru2 "github.com/alibaba/sentinel-golang/core/hotspot/cache/lru" ) func testBySize(cacheSize int, zipf *rand.Zipf) { lfu, _ := NewTinyLfu(cacheSize) - lru, _ := NewLRU(cacheSize, nil) + lru, _ := lru2.NewLRU(cacheSize, nil) totalLfu := 0 missLfu := 0 for i := 0; i < 2000000; i++ { diff --git a/core/hotspot/cache/slru.go b/core/hotspot/cache/wtinylfu/slru.go similarity index 99% rename from core/hotspot/cache/slru.go rename to core/hotspot/cache/wtinylfu/slru.go index 3c94f2c6b..c5c8519a2 100644 --- a/core/hotspot/cache/slru.go +++ b/core/hotspot/cache/wtinylfu/slru.go @@ -1,4 +1,4 @@ -package cache +package wtinylfu import "container/list" diff --git a/core/hotspot/cache/tinylfu.go b/core/hotspot/cache/wtinylfu/tinylfu.go similarity index 99% rename from core/hotspot/cache/tinylfu.go rename to core/hotspot/cache/wtinylfu/tinylfu.go index 4205ad7e7..95811cbaa 100644 --- a/core/hotspot/cache/tinylfu.go +++ b/core/hotspot/cache/wtinylfu/tinylfu.go @@ -1,4 +1,4 @@ -package cache +package wtinylfu import ( "container/list" diff --git a/core/hotspot/cache/tinylfu_test.go b/core/hotspot/cache/wtinylfu/tinylfu_test.go similarity index 99% rename from core/hotspot/cache/tinylfu_test.go rename to core/hotspot/cache/wtinylfu/tinylfu_test.go index e808d6ac3..5632df7c2 100644 --- a/core/hotspot/cache/tinylfu_test.go +++ b/core/hotspot/cache/wtinylfu/tinylfu_test.go @@ -1,4 +1,4 @@ -package cache +package wtinylfu import ( "testing" diff --git a/core/hotspot/rule_manager_test.go b/core/hotspot/rule_manager_test.go index 5f48be6ff..7d945907b 100644 --- a/core/hotspot/rule_manager_test.go +++ b/core/hotspot/rule_manager_test.go @@ -5,7 +5,7 @@ import ( "math" "testing" - "github.com/alibaba/sentinel-golang/core/hotspot/cache" + "github.com/alibaba/sentinel-golang/core/hotspot/cache/wtinylfu" "github.com/stretchr/testify/assert" ) @@ -60,9 +60,9 @@ func Test_tcGenFuncMap(t *testing.T) { size = ParamsMaxCapacity } metric := &ParamsMetric{ - RuleTimeCounter: cache.NewTinyLfuCacheMap(size), - RuleTokenCounter: cache.NewTinyLfuCacheMap(size), - ConcurrencyCounter: cache.NewTinyLfuCacheMap(ConcurrencyMaxCount), + RuleTimeCounter: wtinylfu.NewTinyLfuCacheMap(size), + RuleTokenCounter: wtinylfu.NewTinyLfuCacheMap(size), + ConcurrencyCounter: wtinylfu.NewTinyLfuCacheMap(ConcurrencyMaxCount), } tc := generator(r1, metric) diff --git a/core/hotspot/traffic_shaping.go b/core/hotspot/traffic_shaping.go index 80355fa76..4edca28c7 100644 --- a/core/hotspot/traffic_shaping.go +++ b/core/hotspot/traffic_shaping.go @@ -7,7 +7,7 @@ import ( "sync/atomic" "github.com/alibaba/sentinel-golang/core/base" - "github.com/alibaba/sentinel-golang/core/hotspot/cache" + "github.com/alibaba/sentinel-golang/core/hotspot/cache/wtinylfu" "github.com/alibaba/sentinel-golang/logging" "github.com/alibaba/sentinel-golang/util" "github.com/pkg/errors" @@ -69,8 +69,8 @@ func newBaseTrafficShapingController(r *Rule) *baseTrafficShapingController { size = ParamsMaxCapacity } metric := &ParamsMetric{ - RuleTimeCounter: cache.NewTinyLfuCacheMap(size), - RuleTokenCounter: cache.NewTinyLfuCacheMap(size), + RuleTimeCounter: wtinylfu.NewTinyLfuCacheMap(size), + RuleTokenCounter: wtinylfu.NewTinyLfuCacheMap(size), } return newBaseTrafficShapingControllerWithMetric(r, metric) case Concurrency: @@ -81,7 +81,7 @@ func newBaseTrafficShapingController(r *Rule) *baseTrafficShapingController { size = ConcurrencyMaxCount } metric := &ParamsMetric{ - ConcurrencyCounter: cache.NewTinyLfuCacheMap(size), + ConcurrencyCounter: wtinylfu.NewTinyLfuCacheMap(size), } return newBaseTrafficShapingControllerWithMetric(r, metric) default: From b54c5606dfad5b98bb601c80a245b35e84075700 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Mon, 14 Dec 2020 21:12:59 +0800 Subject: [PATCH 11/24] merge master --- core/hotspot/cache/lru/concurrent_lru.go | 2 +- core/hotspot/cache/lru/concurrent_lru_benchmark_test.go | 2 +- core/hotspot/cache/lru/concurrent_lru_test.go | 2 +- core/hotspot/cache/lru/lru.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/hotspot/cache/lru/concurrent_lru.go b/core/hotspot/cache/lru/concurrent_lru.go index bfdc2e346..bec815bd6 100644 --- a/core/hotspot/cache/lru/concurrent_lru.go +++ b/core/hotspot/cache/lru/concurrent_lru.go @@ -92,7 +92,7 @@ func (c *LruCacheMap) Purge() { c.lru.Purge() } -func NewLRUCacheMap(size int) ConcurrentCounterCache { +func NewLRUCacheMap(size int) cache.ConcurrentCounterCache { lru, err := NewLRU(size, nil) if err != nil { return nil diff --git a/core/hotspot/cache/lru/concurrent_lru_benchmark_test.go b/core/hotspot/cache/lru/concurrent_lru_benchmark_test.go index 0d07d90dd..32ab4a423 100644 --- a/core/hotspot/cache/lru/concurrent_lru_benchmark_test.go +++ b/core/hotspot/cache/lru/concurrent_lru_benchmark_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cache +package lru import ( "strconv" diff --git a/core/hotspot/cache/lru/concurrent_lru_test.go b/core/hotspot/cache/lru/concurrent_lru_test.go index cdec3081a..aadcaf161 100644 --- a/core/hotspot/cache/lru/concurrent_lru_test.go +++ b/core/hotspot/cache/lru/concurrent_lru_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cache +package lru import ( "strconv" diff --git a/core/hotspot/cache/lru/lru.go b/core/hotspot/cache/lru/lru.go index f1ef53033..cb8e61e90 100644 --- a/core/hotspot/cache/lru/lru.go +++ b/core/hotspot/cache/lru/lru.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cache +package lru import ( "container/list" From 69b9dd66cb553a9a660b6afc5840032c6da9ebd5 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Sat, 19 Dec 2020 12:49:16 +0800 Subject: [PATCH 12/24] refine --- .../cache/wtinylfu/concurrent_tinylfu.go | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go b/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go index c8204318a..0daa1fa5b 100644 --- a/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go +++ b/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go @@ -10,20 +10,20 @@ import ( type TinyLfuCacheMap struct { // Not thread safe tinyLfu *TinyLfu - lock *sync.RWMutex + sync.RWMutex } func (c *TinyLfuCacheMap) Add(key interface{}, value *int64) { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock() + defer c.Unlock() c.tinyLfu.Add(key, value) return } func (c *TinyLfuCacheMap) AddIfAbsent(key interface{}, value *int64) (priorValue *int64) { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock() + defer c.Unlock() val := c.tinyLfu.AddIfAbsent(key, value) if val == nil { return nil @@ -33,8 +33,8 @@ func (c *TinyLfuCacheMap) AddIfAbsent(key interface{}, value *int64) (priorValue } func (c *TinyLfuCacheMap) Get(key interface{}) (value *int64, isFound bool) { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock() + defer c.Unlock() val, found := c.tinyLfu.Get(key) if found { @@ -44,36 +44,36 @@ func (c *TinyLfuCacheMap) Get(key interface{}) (value *int64, isFound bool) { } func (c *TinyLfuCacheMap) Remove(key interface{}) (isFound bool) { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock() + defer c.Unlock() return c.tinyLfu.Remove(key) } func (c *TinyLfuCacheMap) Contains(key interface{}) (ok bool) { - c.lock.RLock() - defer c.lock.RUnlock() + c.RLock() + defer c.RUnlock() return c.tinyLfu.Contains(key) } func (c *TinyLfuCacheMap) Keys() []interface{} { - c.lock.RLock() - defer c.lock.RUnlock() + c.RLock() + defer c.RUnlock() return c.tinyLfu.Keys() } func (c *TinyLfuCacheMap) Len() int { - c.lock.RLock() - defer c.lock.RUnlock() + c.RLock() + defer c.RUnlock() return c.tinyLfu.Len() } func (c *TinyLfuCacheMap) Purge() { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock() + defer c.Unlock() c.tinyLfu.Purge() } @@ -85,6 +85,5 @@ func NewTinyLfuCacheMap(size int) cache.ConcurrentCounterCache { } return &TinyLfuCacheMap{ tinyLfu: tinyLfu, - lock: new(sync.RWMutex), } } From 7b3466af74c7569cc51fc383550a251871ef15b9 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Sat, 19 Dec 2020 13:02:33 +0800 Subject: [PATCH 13/24] add License --- core/hotspot/cache/wtinylfu/concurrent_tinylfu.go | 14 ++++++++++++++ .../wtinylfu/concurrent_tinylfu_benchmark_test.go | 14 ++++++++++++++ core/hotspot/cache/wtinylfu/count_min_sketch.go | 14 ++++++++++++++ .../cache/wtinylfu/count_min_sketch_test.go | 14 ++++++++++++++ core/hotspot/cache/wtinylfu/doorkeeper.go | 14 ++++++++++++++ core/hotspot/cache/wtinylfu/doorkeeper_test.go | 14 ++++++++++++++ core/hotspot/cache/wtinylfu/hash.go | 14 ++++++++++++++ core/hotspot/cache/wtinylfu/hash_benchmark_test.go | 14 ++++++++++++++ core/hotspot/cache/wtinylfu/hitrate_test.go | 14 ++++++++++++++ core/hotspot/cache/wtinylfu/slru.go | 14 ++++++++++++++ core/hotspot/cache/wtinylfu/tinylfu.go | 14 ++++++++++++++ core/hotspot/cache/wtinylfu/tinylfu_test.go | 14 ++++++++++++++ 12 files changed, 168 insertions(+) diff --git a/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go b/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go index 0daa1fa5b..485be65b2 100644 --- a/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go +++ b/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import ( diff --git a/core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go b/core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go index 0b44f77a9..64899e2d8 100644 --- a/core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go +++ b/core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import ( diff --git a/core/hotspot/cache/wtinylfu/count_min_sketch.go b/core/hotspot/cache/wtinylfu/count_min_sketch.go index 879245fd6..a41db10f1 100644 --- a/core/hotspot/cache/wtinylfu/count_min_sketch.go +++ b/core/hotspot/cache/wtinylfu/count_min_sketch.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu const sketchDepth = 4 diff --git a/core/hotspot/cache/wtinylfu/count_min_sketch_test.go b/core/hotspot/cache/wtinylfu/count_min_sketch_test.go index 527b9f913..b50d008f2 100644 --- a/core/hotspot/cache/wtinylfu/count_min_sketch_test.go +++ b/core/hotspot/cache/wtinylfu/count_min_sketch_test.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import ( diff --git a/core/hotspot/cache/wtinylfu/doorkeeper.go b/core/hotspot/cache/wtinylfu/doorkeeper.go index fbc86e1f9..af907bede 100644 --- a/core/hotspot/cache/wtinylfu/doorkeeper.go +++ b/core/hotspot/cache/wtinylfu/doorkeeper.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import "math" diff --git a/core/hotspot/cache/wtinylfu/doorkeeper_test.go b/core/hotspot/cache/wtinylfu/doorkeeper_test.go index be803db4f..9ca724eaf 100644 --- a/core/hotspot/cache/wtinylfu/doorkeeper_test.go +++ b/core/hotspot/cache/wtinylfu/doorkeeper_test.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import ( diff --git a/core/hotspot/cache/wtinylfu/hash.go b/core/hotspot/cache/wtinylfu/hash.go index fc9de38c8..2f19f371c 100644 --- a/core/hotspot/cache/wtinylfu/hash.go +++ b/core/hotspot/cache/wtinylfu/hash.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import ( diff --git a/core/hotspot/cache/wtinylfu/hash_benchmark_test.go b/core/hotspot/cache/wtinylfu/hash_benchmark_test.go index 2132469b0..f40c83dbd 100644 --- a/core/hotspot/cache/wtinylfu/hash_benchmark_test.go +++ b/core/hotspot/cache/wtinylfu/hash_benchmark_test.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import ( diff --git a/core/hotspot/cache/wtinylfu/hitrate_test.go b/core/hotspot/cache/wtinylfu/hitrate_test.go index edebf7cb6..e821dbc0a 100644 --- a/core/hotspot/cache/wtinylfu/hitrate_test.go +++ b/core/hotspot/cache/wtinylfu/hitrate_test.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import ( diff --git a/core/hotspot/cache/wtinylfu/slru.go b/core/hotspot/cache/wtinylfu/slru.go index c5c8519a2..097ff6e75 100644 --- a/core/hotspot/cache/wtinylfu/slru.go +++ b/core/hotspot/cache/wtinylfu/slru.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import "container/list" diff --git a/core/hotspot/cache/wtinylfu/tinylfu.go b/core/hotspot/cache/wtinylfu/tinylfu.go index 95811cbaa..4b8e4f0cd 100644 --- a/core/hotspot/cache/wtinylfu/tinylfu.go +++ b/core/hotspot/cache/wtinylfu/tinylfu.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import ( diff --git a/core/hotspot/cache/wtinylfu/tinylfu_test.go b/core/hotspot/cache/wtinylfu/tinylfu_test.go index 5632df7c2..47b84569f 100644 --- a/core/hotspot/cache/wtinylfu/tinylfu_test.go +++ b/core/hotspot/cache/wtinylfu/tinylfu_test.go @@ -1,3 +1,17 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package wtinylfu import ( From 6be7145cc8735b0928dd6104893025bbf8fb7ae8 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Sat, 19 Dec 2020 18:35:09 +0800 Subject: [PATCH 14/24] add cache stats --- core/hotspot/cache/concurrent_cache.go | 5 ++ core/hotspot/cache/lru/concurrent_lru.go | 46 +++++----- core/hotspot/cache/lru/lru.go | 13 +++ core/hotspot/cache/stats/cache_stats.go | 90 +++++++++++++++++++ core/hotspot/cache/stats/cache_stats_test.go | 54 +++++++++++ .../cache/wtinylfu/concurrent_tinylfu.go | 7 ++ core/hotspot/cache/wtinylfu/hitrate_test.go | 16 ++-- core/hotspot/cache/wtinylfu/tinylfu.go | 23 ++++- 8 files changed, 222 insertions(+), 32 deletions(-) create mode 100644 core/hotspot/cache/stats/cache_stats.go create mode 100644 core/hotspot/cache/stats/cache_stats_test.go diff --git a/core/hotspot/cache/concurrent_cache.go b/core/hotspot/cache/concurrent_cache.go index de96e6c65..7c64e00a4 100644 --- a/core/hotspot/cache/concurrent_cache.go +++ b/core/hotspot/cache/concurrent_cache.go @@ -14,6 +14,8 @@ package cache +import "github.com/alibaba/sentinel-golang/core/hotspot/cache/stats" + // ConcurrentCounterCache cache the hotspot parameter type ConcurrentCounterCache interface { // Add add a value to the cache, @@ -43,4 +45,7 @@ type ConcurrentCounterCache interface { // Purge clears all cache entries. Purge() + + // Stats copies cache stats. + Stats() *stats.CacheStats } diff --git a/core/hotspot/cache/lru/concurrent_lru.go b/core/hotspot/cache/lru/concurrent_lru.go index bec815bd6..8b344fe3d 100644 --- a/core/hotspot/cache/lru/concurrent_lru.go +++ b/core/hotspot/cache/lru/concurrent_lru.go @@ -18,26 +18,27 @@ import ( "sync" "github.com/alibaba/sentinel-golang/core/hotspot/cache" + "github.com/alibaba/sentinel-golang/core/hotspot/cache/stats" ) // LruCacheMap use LRU strategy to cache the most frequently accessed hotspot parameter type LruCacheMap struct { // Not thread safe - lru *LRU - lock *sync.RWMutex + lru *LRU + sync.RWMutex } func (c *LruCacheMap) Add(key interface{}, value *int64) { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock() + defer c.Unlock() c.lru.Add(key, value) return } func (c *LruCacheMap) AddIfAbsent(key interface{}, value *int64) (priorValue *int64) { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock() + defer c.Unlock() val := c.lru.AddIfAbsent(key, value) if val == nil { return nil @@ -47,8 +48,8 @@ func (c *LruCacheMap) AddIfAbsent(key interface{}, value *int64) (priorValue *in } func (c *LruCacheMap) Get(key interface{}) (value *int64, isFound bool) { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock() + defer c.Unlock() val, found := c.lru.Get(key) if found { @@ -58,47 +59,52 @@ func (c *LruCacheMap) Get(key interface{}) (value *int64, isFound bool) { } func (c *LruCacheMap) Remove(key interface{}) (isFound bool) { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock() + defer c.Unlock() return c.lru.Remove(key) } func (c *LruCacheMap) Contains(key interface{}) (ok bool) { - c.lock.RLock() - defer c.lock.RUnlock() + c.RLock() + defer c.RUnlock() return c.lru.Contains(key) } func (c *LruCacheMap) Keys() []interface{} { - c.lock.RLock() - defer c.lock.RUnlock() + c.RLock() + defer c.RUnlock() return c.lru.Keys() } func (c *LruCacheMap) Len() int { - c.lock.RLock() - defer c.lock.RUnlock() + c.RLock() + defer c.RUnlock() return c.lru.Len() } func (c *LruCacheMap) Purge() { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock() + defer c.Unlock() c.lru.Purge() } +func (c *LruCacheMap) Stats() *stats.CacheStats { + c.RUnlock() + defer c.RUnlock() + return c.lru.Stats() +} + func NewLRUCacheMap(size int) cache.ConcurrentCounterCache { lru, err := NewLRU(size, nil) if err != nil { return nil } return &LruCacheMap{ - lru: lru, - lock: new(sync.RWMutex), + lru: lru, } } diff --git a/core/hotspot/cache/lru/lru.go b/core/hotspot/cache/lru/lru.go index cb8e61e90..52c2d8ac3 100644 --- a/core/hotspot/cache/lru/lru.go +++ b/core/hotspot/cache/lru/lru.go @@ -17,6 +17,7 @@ package lru import ( "container/list" + "github.com/alibaba/sentinel-golang/core/hotspot/cache/stats" "github.com/pkg/errors" ) @@ -29,6 +30,7 @@ type LRU struct { evictList *list.List items map[interface{}]*list.Element onEvict EvictCallback + stats *stats.CacheStats } // entry is used to hold a value in the evictList @@ -47,6 +49,7 @@ func NewLRU(size int, onEvict EvictCallback) (*LRU, error) { evictList: list.New(), items: make(map[interface{}]*list.Element, 64), onEvict: onEvict, + stats: stats.NewCacheStats(), } return c, nil } @@ -78,6 +81,7 @@ func (c *LRU) Add(key, value interface{}) { evict := c.evictList.Len() > c.size // Verify size not exceeded if evict { + c.stats.RecordEviction() c.removeOldest() } return @@ -100,6 +104,7 @@ func (c *LRU) AddIfAbsent(key interface{}, value interface{}) (priorValue interf evict := c.evictList.Len() > c.size // Verify size not exceeded if evict { + c.stats.RecordEviction() c.removeOldest() } return nil @@ -110,10 +115,13 @@ func (c *LRU) Get(key interface{}) (value interface{}, isFound bool) { if ent, ok := c.items[key]; ok { c.evictList.MoveToFront(ent) if ent.Value.(*entry) == nil { + c.stats.RecordMisses() return nil, false } + c.stats.RecordHits() return ent.Value.(*entry).value, true } + c.stats.RecordMisses() return } @@ -217,3 +225,8 @@ func (c *LRU) removeElement(e *list.Element) { c.onEvict(kv.key, kv.value) } } + +// Stats copies cache stats. +func (c LRU) Stats() *stats.CacheStats { + return c.stats.Snapshot() +} diff --git a/core/hotspot/cache/stats/cache_stats.go b/core/hotspot/cache/stats/cache_stats.go new file mode 100644 index 000000000..be4f1d4c3 --- /dev/null +++ b/core/hotspot/cache/stats/cache_stats.go @@ -0,0 +1,90 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stats + +import ( + "sync/atomic" +) + +// CacheStats is statistics about a cache. +type CacheStats struct { + hitCount *uint64 + missCount *uint64 + evictionCount *uint64 +} + +func NewCacheStats() *CacheStats { + return createCacheStats(0, 0, 0) +} + +func createCacheStats(hitCount uint64, missCount uint64, evictionCount uint64) *CacheStats { + cs := &CacheStats{ + hitCount: new(uint64), + missCount: new(uint64), + evictionCount: new(uint64), + } + *cs.hitCount = hitCount + *cs.missCount = missCount + *cs.evictionCount = evictionCount + return cs +} + +func (s *CacheStats) HitCount() uint64 { + return atomic.LoadUint64(s.hitCount) +} + +func (s *CacheStats) MissCount() uint64 { + return atomic.LoadUint64(s.missCount) +} + +func (s *CacheStats) RequestCount() uint64 { + return s.HitCount() + s.MissCount() +} + +func (s *CacheStats) EvictionCount() uint64 { + return atomic.LoadUint64(s.evictionCount) +} + +func (s *CacheStats) HitRate() float64 { + requestCount := s.RequestCount() + if requestCount == 0 { + return 1.0 + } + return float64(s.HitCount()) / float64(requestCount) +} + +func (s *CacheStats) MissRate() float64 { + requestCount := s.RequestCount() + if requestCount == 0 { + return 0.0 + } + return float64(s.MissCount()) / float64(requestCount) +} + +func (s *CacheStats) RecordHits() { + atomic.AddUint64(s.hitCount, 1) +} + +func (s *CacheStats) RecordMisses() { + atomic.AddUint64(s.missCount, 1) +} + +func (s *CacheStats) RecordEviction() { + atomic.AddUint64(s.evictionCount, 1) +} + +func (s *CacheStats) Snapshot() *CacheStats { + return createCacheStats(s.HitCount(), s.MissCount(), s.EvictionCount()) +} diff --git a/core/hotspot/cache/stats/cache_stats_test.go b/core/hotspot/cache/stats/cache_stats_test.go new file mode 100644 index 000000000..8fd880348 --- /dev/null +++ b/core/hotspot/cache/stats/cache_stats_test.go @@ -0,0 +1,54 @@ +// Copyright 1999-2020 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stats + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCacheStats(t *testing.T) { + t.Run("Test_CacheStats", func(t *testing.T) { + cs := NewCacheStats() + for i := 0; i < 100; i++ { + cs.RecordMisses() + } + assert.True(t, cs.MissCount() == 100) + assert.True(t, cs.MissRate() == 1.0) + assert.True(t, cs.HitRate() == 0.0) + + for i := 0; i < 100; i++ { + cs.RecordHits() + } + + assert.True(t, cs.MissCount() == 100) + assert.True(t, cs.HitRate() == 100) + assert.True(t, cs.MissRate() == 0.5) + assert.True(t, cs.HitRate() == 0.5) + + for i := 0; i < 50; i++ { + cs.RecordEviction() + } + assert.True(t, cs.EvictionCount() == 50) + + csNap := cs.Snapshot() + for i := 0; i < 50; i++ { + cs.RecordEviction() + } + assert.True(t, cs.EvictionCount() == 100) + assert.True(t, csNap.EvictionCount() == 50) + }) +} diff --git a/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go b/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go index 485be65b2..a98596ff1 100644 --- a/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go +++ b/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go @@ -18,6 +18,7 @@ import ( "sync" "github.com/alibaba/sentinel-golang/core/hotspot/cache" + "github.com/alibaba/sentinel-golang/core/hotspot/cache/stats" ) // TinyLfuCacheMap use tinyLfu strategy to cache the most frequently accessed hotspot parameter @@ -92,6 +93,12 @@ func (c *TinyLfuCacheMap) Purge() { c.tinyLfu.Purge() } +func (c *TinyLfuCacheMap) Stats() *stats.CacheStats { + c.RUnlock() + defer c.RUnlock() + return c.tinyLfu.Stats() +} + func NewTinyLfuCacheMap(size int) cache.ConcurrentCounterCache { tinyLfu, err := NewTinyLfu(size) if err != nil { diff --git a/core/hotspot/cache/wtinylfu/hitrate_test.go b/core/hotspot/cache/wtinylfu/hitrate_test.go index e821dbc0a..58bd246ca 100644 --- a/core/hotspot/cache/wtinylfu/hitrate_test.go +++ b/core/hotspot/cache/wtinylfu/hitrate_test.go @@ -25,32 +25,28 @@ import ( func testBySize(cacheSize int, zipf *rand.Zipf) { lfu, _ := NewTinyLfu(cacheSize) lru, _ := lru2.NewLRU(cacheSize, nil) - totalLfu := 0 - missLfu := 0 for i := 0; i < 2000000; i++ { - totalLfu++ key := zipf.Uint64() _, ok := lfu.Get(key) if !ok { - missLfu++ lfu.Add(key, key) } } + s := lfu.Stats() - fmt.Printf("tinyLfu cache size %d, total %d, miss %d, hitRate %f \n", cacheSize, totalLfu, missLfu, (float64(totalLfu)-float64(missLfu))/float64(totalLfu)) + fmt.Printf("tinyLfu cache size %d, hit %d, miss %d, evictionCount %d, hitRate %f \n", cacheSize, s.HitCount(), + s.MissCount(), s.EvictionCount(), s.HitRate()) - totalLru := 0 - missLru := 0 for i := 0; i < 2000000; i++ { - totalLru++ key := zipf.Uint64() _, ok := lru.Get(key) if !ok { - missLru++ lru.Add(key, key) } } - fmt.Printf("lru cache size %d, total %d, miss %d, hitRate %f \n \n", cacheSize, totalLru, missLru, (float64(totalLru)-float64(missLru))/float64(totalLru)) + st := lru.Stats() + fmt.Printf("lru cache size %d, hit %d, miss %d, evictionCount %d, hitRate %f \n", cacheSize, st.HitCount(), + st.MissCount(), st.EvictionCount(), st.HitRate()) } func TestHitRate(t *testing.T) { diff --git a/core/hotspot/cache/wtinylfu/tinylfu.go b/core/hotspot/cache/wtinylfu/tinylfu.go index 4b8e4f0cd..a4fc2e635 100644 --- a/core/hotspot/cache/wtinylfu/tinylfu.go +++ b/core/hotspot/cache/wtinylfu/tinylfu.go @@ -17,6 +17,8 @@ package wtinylfu import ( "container/list" "errors" + + "github.com/alibaba/sentinel-golang/core/hotspot/cache/stats" ) const ( @@ -46,6 +48,7 @@ type TinyLfu struct { lru *lru slru *slru items map[interface{}]*list.Element + stats *stats.CacheStats } func NewTinyLfu(cap int) (*TinyLfu, error) { @@ -67,11 +70,16 @@ func NewTinyLfu(cap int) (*TinyLfu, error) { items: items, lru: newLRU(lruCap, items), slru: newSLRU(slruSize, items), + stats: stats.NewCacheStats(), }, nil } // Get looks up a key's value from the cache. func (t *TinyLfu) Get(key interface{}) (interface{}, bool) { + return t.get(key, false) +} + +func (t *TinyLfu) get(key interface{}, isInternal bool) (interface{}, bool) { t.additions++ if t.additions == t.samples { t.countMinSketch.reset() @@ -85,6 +93,9 @@ func (t *TinyLfu) Get(key interface{}) (interface{}, bool) { if t.doorkeeper.put(keyHash) { t.countMinSketch.add(keyHash) } + if !isInternal { + t.stats.RecordMisses() + } return nil, false } item := val.Value.(*slruItem) @@ -98,7 +109,9 @@ func (t *TinyLfu) Get(key interface{}) (interface{}, bool) { } else { t.slru.get(val) } - + if !isInternal { + t.stats.RecordHits() + } return v, true } @@ -116,7 +129,7 @@ func (t *TinyLfu) Add(key interface{}, val interface{}) { func (t *TinyLfu) AddIfAbsent(key interface{}, val interface{}) (priorValue interface{}) { // Check for existing item - if v, ok := t.Get(key); ok { + if v, ok := t.get(key, true); ok { return v } @@ -138,6 +151,7 @@ func (t *TinyLfu) AddIfAbsent(key interface{}, val interface{}) (priorValue inte if candidateCount > victimCount { t.slru.add(candidate) } + t.stats.RecordEviction() return nil } @@ -194,3 +208,8 @@ func (t *TinyLfu) Purge() { t.doorkeeper.reset() t.countMinSketch.clear() } + +// Stats copies cache stats. +func (t *TinyLfu) Stats() *stats.CacheStats { + return t.stats.Snapshot() +} From 1e769f3fb2ba2ed412c416b815993709dc116518 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Sat, 19 Dec 2020 18:47:38 +0800 Subject: [PATCH 15/24] fix ci --- core/hotspot/traffic_shaping_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/hotspot/traffic_shaping_test.go b/core/hotspot/traffic_shaping_test.go index 8ab99fadb..b71720050 100644 --- a/core/hotspot/traffic_shaping_test.go +++ b/core/hotspot/traffic_shaping_test.go @@ -19,6 +19,8 @@ import ( "testing" "time" + "github.com/alibaba/sentinel-golang/core/hotspot/cache/stats" + "github.com/alibaba/sentinel-golang/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -76,6 +78,11 @@ func (c *counterCacheMock) Purge() { return } +func (c *counterCacheMock) Stats() *stats.CacheStats { + _ = c.Called() + return nil +} + func Test_baseTrafficShapingController_performCheckingForConcurrencyMetric(t *testing.T) { t.Run("Test_baseTrafficShapingController_performCheckingForConcurrencyMetric", func(t *testing.T) { goCounter := &counterCacheMock{} From 8bf607e50fc20f857263b44b4d96eadfda7f7045 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Sat, 19 Dec 2020 18:53:54 +0800 Subject: [PATCH 16/24] fix test --- core/hotspot/cache/stats/cache_stats_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hotspot/cache/stats/cache_stats_test.go b/core/hotspot/cache/stats/cache_stats_test.go index 8fd880348..e47967865 100644 --- a/core/hotspot/cache/stats/cache_stats_test.go +++ b/core/hotspot/cache/stats/cache_stats_test.go @@ -35,7 +35,7 @@ func TestCacheStats(t *testing.T) { } assert.True(t, cs.MissCount() == 100) - assert.True(t, cs.HitRate() == 100) + assert.True(t, cs.HitCount() == 100) assert.True(t, cs.MissRate() == 0.5) assert.True(t, cs.HitRate() == 0.5) From 6fd1358cb594a944835703275958607976a43d9c Mon Sep 17 00:00:00 2001 From: liqiangz Date: Mon, 21 Dec 2020 20:03:53 +0800 Subject: [PATCH 17/24] add isRecordingStats params --- core/hotspot/cache/concurrent_cache.go | 2 +- core/hotspot/cache/lru/concurrent_lru.go | 6 ++-- core/hotspot/cache/lru/lru.go | 35 ++++++++++++++----- .../cache/wtinylfu/concurrent_tinylfu.go | 6 ++-- .../concurrent_tinylfu_benchmark_test.go | 6 ++-- core/hotspot/cache/wtinylfu/hitrate_test.go | 8 ++--- core/hotspot/cache/wtinylfu/tinylfu.go | 24 ++++++++----- core/hotspot/cache/wtinylfu/tinylfu_test.go | 2 +- core/hotspot/rule_manager_test.go | 6 ++-- core/hotspot/traffic_shaping.go | 6 ++-- core/hotspot/traffic_shaping_test.go | 4 +-- 11 files changed, 65 insertions(+), 40 deletions(-) diff --git a/core/hotspot/cache/concurrent_cache.go b/core/hotspot/cache/concurrent_cache.go index 7c64e00a4..704fc1be3 100644 --- a/core/hotspot/cache/concurrent_cache.go +++ b/core/hotspot/cache/concurrent_cache.go @@ -47,5 +47,5 @@ type ConcurrentCounterCache interface { Purge() // Stats copies cache stats. - Stats() *stats.CacheStats + Stats() (*stats.CacheStats, error) } diff --git a/core/hotspot/cache/lru/concurrent_lru.go b/core/hotspot/cache/lru/concurrent_lru.go index 8b344fe3d..80fa7f9f1 100644 --- a/core/hotspot/cache/lru/concurrent_lru.go +++ b/core/hotspot/cache/lru/concurrent_lru.go @@ -93,14 +93,14 @@ func (c *LruCacheMap) Purge() { c.lru.Purge() } -func (c *LruCacheMap) Stats() *stats.CacheStats { +func (c *LruCacheMap) Stats() (*stats.CacheStats, error) { c.RUnlock() defer c.RUnlock() return c.lru.Stats() } -func NewLRUCacheMap(size int) cache.ConcurrentCounterCache { - lru, err := NewLRU(size, nil) +func NewLRUCacheMap(size int, isRecordingStats bool) cache.ConcurrentCounterCache { + lru, err := NewLRU(size, nil, isRecordingStats) if err != nil { return nil } diff --git a/core/hotspot/cache/lru/lru.go b/core/hotspot/cache/lru/lru.go index 52c2d8ac3..19149b033 100644 --- a/core/hotspot/cache/lru/lru.go +++ b/core/hotspot/cache/lru/lru.go @@ -40,16 +40,20 @@ type entry struct { } // NewLRU constructs an LRU of the given size -func NewLRU(size int, onEvict EvictCallback) (*LRU, error) { +func NewLRU(size int, onEvict EvictCallback, isRecordingStats bool) (*LRU, error) { if size <= 0 { return nil, errors.New("must provide a positive size") } + var statsCache *stats.CacheStats + if isRecordingStats { + statsCache = stats.NewCacheStats() + } c := &LRU{ size: size, evictList: list.New(), items: make(map[interface{}]*list.Element, 64), onEvict: onEvict, - stats: stats.NewCacheStats(), + stats: statsCache, } return c, nil } @@ -81,7 +85,9 @@ func (c *LRU) Add(key, value interface{}) { evict := c.evictList.Len() > c.size // Verify size not exceeded if evict { - c.stats.RecordEviction() + if c.stats != nil { + c.stats.RecordEviction() + } c.removeOldest() } return @@ -104,7 +110,9 @@ func (c *LRU) AddIfAbsent(key interface{}, value interface{}) (priorValue interf evict := c.evictList.Len() > c.size // Verify size not exceeded if evict { - c.stats.RecordEviction() + if c.stats != nil { + c.stats.RecordEviction() + } c.removeOldest() } return nil @@ -115,13 +123,19 @@ func (c *LRU) Get(key interface{}) (value interface{}, isFound bool) { if ent, ok := c.items[key]; ok { c.evictList.MoveToFront(ent) if ent.Value.(*entry) == nil { - c.stats.RecordMisses() + if c.stats != nil { + c.stats.RecordMisses() + } return nil, false } - c.stats.RecordHits() + if c.stats != nil { + c.stats.RecordHits() + } return ent.Value.(*entry).value, true } - c.stats.RecordMisses() + if c.stats != nil { + c.stats.RecordMisses() + } return } @@ -227,6 +241,9 @@ func (c *LRU) removeElement(e *list.Element) { } // Stats copies cache stats. -func (c LRU) Stats() *stats.CacheStats { - return c.stats.Snapshot() +func (c LRU) Stats() (*stats.CacheStats, error) { + if c.stats == nil { + return nil, errors.New("RecordingStats Must be enabled") + } + return c.stats.Snapshot(), nil } diff --git a/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go b/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go index a98596ff1..65f7d3caf 100644 --- a/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go +++ b/core/hotspot/cache/wtinylfu/concurrent_tinylfu.go @@ -93,14 +93,14 @@ func (c *TinyLfuCacheMap) Purge() { c.tinyLfu.Purge() } -func (c *TinyLfuCacheMap) Stats() *stats.CacheStats { +func (c *TinyLfuCacheMap) Stats() (*stats.CacheStats, error) { c.RUnlock() defer c.RUnlock() return c.tinyLfu.Stats() } -func NewTinyLfuCacheMap(size int) cache.ConcurrentCounterCache { - tinyLfu, err := NewTinyLfu(size) +func NewTinyLfuCacheMap(size int, isRecordingStats bool) cache.ConcurrentCounterCache { + tinyLfu, err := NewTinyLfu(size, isRecordingStats) if err != nil { return nil } diff --git a/core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go b/core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go index 64899e2d8..84042ba2e 100644 --- a/core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go +++ b/core/hotspot/cache/wtinylfu/concurrent_tinylfu_benchmark_test.go @@ -22,7 +22,7 @@ import ( const Size = 50000 func Benchmark_TINYLFU_AddIfAbsent(b *testing.B) { - c := NewTinyLfuCacheMap(Size) + c := NewTinyLfuCacheMap(Size, false) for a := 1; a <= Size; a++ { val := new(int64) *val = int64(a) @@ -42,7 +42,7 @@ func Benchmark_TINYLFU_AddIfAbsent(b *testing.B) { } func Benchmark_TINYLFU_Add(b *testing.B) { - c := NewTinyLfuCacheMap(Size) + c := NewTinyLfuCacheMap(Size, false) for a := 1; a <= Size; a++ { val := new(int64) *val = int64(a) @@ -59,7 +59,7 @@ func Benchmark_TINYLFU_Add(b *testing.B) { } func Benchmark_TINYLFU_Get(b *testing.B) { - c := NewTinyLfuCacheMap(Size) + c := NewTinyLfuCacheMap(Size, false) for a := 1; a <= Size; a++ { val := new(int64) *val = int64(a) diff --git a/core/hotspot/cache/wtinylfu/hitrate_test.go b/core/hotspot/cache/wtinylfu/hitrate_test.go index 58bd246ca..d142c31c8 100644 --- a/core/hotspot/cache/wtinylfu/hitrate_test.go +++ b/core/hotspot/cache/wtinylfu/hitrate_test.go @@ -23,8 +23,8 @@ import ( ) func testBySize(cacheSize int, zipf *rand.Zipf) { - lfu, _ := NewTinyLfu(cacheSize) - lru, _ := lru2.NewLRU(cacheSize, nil) + lfu, _ := NewTinyLfu(cacheSize, true) + lru, _ := lru2.NewLRU(cacheSize, nil, true) for i := 0; i < 2000000; i++ { key := zipf.Uint64() _, ok := lfu.Get(key) @@ -32,7 +32,7 @@ func testBySize(cacheSize int, zipf *rand.Zipf) { lfu.Add(key, key) } } - s := lfu.Stats() + s, _ := lfu.Stats() fmt.Printf("tinyLfu cache size %d, hit %d, miss %d, evictionCount %d, hitRate %f \n", cacheSize, s.HitCount(), s.MissCount(), s.EvictionCount(), s.HitRate()) @@ -44,7 +44,7 @@ func testBySize(cacheSize int, zipf *rand.Zipf) { lru.Add(key, key) } } - st := lru.Stats() + st, _ := lru.Stats() fmt.Printf("lru cache size %d, hit %d, miss %d, evictionCount %d, hitRate %f \n", cacheSize, st.HitCount(), st.MissCount(), st.EvictionCount(), st.HitRate()) } diff --git a/core/hotspot/cache/wtinylfu/tinylfu.go b/core/hotspot/cache/wtinylfu/tinylfu.go index a4fc2e635..81c86d778 100644 --- a/core/hotspot/cache/wtinylfu/tinylfu.go +++ b/core/hotspot/cache/wtinylfu/tinylfu.go @@ -51,7 +51,7 @@ type TinyLfu struct { stats *stats.CacheStats } -func NewTinyLfu(cap int) (*TinyLfu, error) { +func NewTinyLfu(cap int, isRecordingStats bool) (*TinyLfu, error) { if cap <= 0 { return nil, errors.New("Must provide a positive size") } @@ -60,7 +60,10 @@ func NewTinyLfu(cap int) (*TinyLfu, error) { } lruCap := int(float64(cap) * lruRatio) slruSize := cap - lruCap - + var statsCache *stats.CacheStats + if isRecordingStats { + statsCache = stats.NewCacheStats() + } items := make(map[interface{}]*list.Element) return &TinyLfu{ countMinSketch: newCountMinSketch(countersFactor * cap), @@ -70,7 +73,7 @@ func NewTinyLfu(cap int) (*TinyLfu, error) { items: items, lru: newLRU(lruCap, items), slru: newSLRU(slruSize, items), - stats: stats.NewCacheStats(), + stats: statsCache, }, nil } @@ -93,7 +96,7 @@ func (t *TinyLfu) get(key interface{}, isInternal bool) (interface{}, bool) { if t.doorkeeper.put(keyHash) { t.countMinSketch.add(keyHash) } - if !isInternal { + if !isInternal && t.stats != nil { t.stats.RecordMisses() } return nil, false @@ -109,7 +112,7 @@ func (t *TinyLfu) get(key interface{}, isInternal bool) (interface{}, bool) { } else { t.slru.get(val) } - if !isInternal { + if !isInternal && t.stats != nil { t.stats.RecordHits() } return v, true @@ -151,7 +154,9 @@ func (t *TinyLfu) AddIfAbsent(key interface{}, val interface{}) (priorValue inte if candidateCount > victimCount { t.slru.add(candidate) } - t.stats.RecordEviction() + if t.stats != nil { + t.stats.RecordEviction() + } return nil } @@ -210,6 +215,9 @@ func (t *TinyLfu) Purge() { } // Stats copies cache stats. -func (t *TinyLfu) Stats() *stats.CacheStats { - return t.stats.Snapshot() +func (t *TinyLfu) Stats() (*stats.CacheStats, error) { + if t.stats == nil { + return nil, errors.New("RecordingStats Must be enabled") + } + return t.stats.Snapshot(), nil } diff --git a/core/hotspot/cache/wtinylfu/tinylfu_test.go b/core/hotspot/cache/wtinylfu/tinylfu_test.go index 47b84569f..64f14610c 100644 --- a/core/hotspot/cache/wtinylfu/tinylfu_test.go +++ b/core/hotspot/cache/wtinylfu/tinylfu_test.go @@ -49,7 +49,7 @@ func TestTinyLFU(t *testing.T) { t.Run("Test_TinyLFU", func(t *testing.T) { s := tinyLFUTest{t: t} - s.lfu, _ = NewTinyLfu(200) + s.lfu, _ = NewTinyLfu(200, false) s.assertCap(200) s.lfu.slru.protectedCap = 2 s.lfu.slru.probationCap = 1 diff --git a/core/hotspot/rule_manager_test.go b/core/hotspot/rule_manager_test.go index bdc7025f2..50ac4b68a 100644 --- a/core/hotspot/rule_manager_test.go +++ b/core/hotspot/rule_manager_test.go @@ -74,9 +74,9 @@ func Test_tcGenFuncMap(t *testing.T) { size = ParamsMaxCapacity } metric := &ParamsMetric{ - RuleTimeCounter: wtinylfu.NewTinyLfuCacheMap(size), - RuleTokenCounter: wtinylfu.NewTinyLfuCacheMap(size), - ConcurrencyCounter: wtinylfu.NewTinyLfuCacheMap(ConcurrencyMaxCount), + RuleTimeCounter: wtinylfu.NewTinyLfuCacheMap(size, false), + RuleTokenCounter: wtinylfu.NewTinyLfuCacheMap(size, false), + ConcurrencyCounter: wtinylfu.NewTinyLfuCacheMap(ConcurrencyMaxCount, false), } tc := generator(r1, metric) diff --git a/core/hotspot/traffic_shaping.go b/core/hotspot/traffic_shaping.go index a0de1070c..c29ba4cb7 100644 --- a/core/hotspot/traffic_shaping.go +++ b/core/hotspot/traffic_shaping.go @@ -84,8 +84,8 @@ func newBaseTrafficShapingController(r *Rule) *baseTrafficShapingController { size = ParamsMaxCapacity } metric := &ParamsMetric{ - RuleTimeCounter: wtinylfu.NewTinyLfuCacheMap(size), - RuleTokenCounter: wtinylfu.NewTinyLfuCacheMap(size), + RuleTimeCounter: wtinylfu.NewTinyLfuCacheMap(size, false), + RuleTokenCounter: wtinylfu.NewTinyLfuCacheMap(size, false), } return newBaseTrafficShapingControllerWithMetric(r, metric) case Concurrency: @@ -96,7 +96,7 @@ func newBaseTrafficShapingController(r *Rule) *baseTrafficShapingController { size = ConcurrencyMaxCount } metric := &ParamsMetric{ - ConcurrencyCounter: wtinylfu.NewTinyLfuCacheMap(size), + ConcurrencyCounter: wtinylfu.NewTinyLfuCacheMap(size, false), } return newBaseTrafficShapingControllerWithMetric(r, metric) default: diff --git a/core/hotspot/traffic_shaping_test.go b/core/hotspot/traffic_shaping_test.go index b71720050..344c4a352 100644 --- a/core/hotspot/traffic_shaping_test.go +++ b/core/hotspot/traffic_shaping_test.go @@ -78,9 +78,9 @@ func (c *counterCacheMock) Purge() { return } -func (c *counterCacheMock) Stats() *stats.CacheStats { +func (c *counterCacheMock) Stats() (*stats.CacheStats, error) { _ = c.Called() - return nil + return nil, nil } func Test_baseTrafficShapingController_performCheckingForConcurrencyMetric(t *testing.T) { From eb5f4f4fea35ca74adda2c87ecdc337d7f42b1cc Mon Sep 17 00:00:00 2001 From: liqiangz Date: Mon, 21 Dec 2020 20:10:03 +0800 Subject: [PATCH 18/24] fix ci --- .../cache/lru/concurrent_lru_benchmark_test.go | 6 +++--- core/hotspot/cache/lru/concurrent_lru_test.go | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/hotspot/cache/lru/concurrent_lru_benchmark_test.go b/core/hotspot/cache/lru/concurrent_lru_benchmark_test.go index 32ab4a423..630c9d254 100644 --- a/core/hotspot/cache/lru/concurrent_lru_benchmark_test.go +++ b/core/hotspot/cache/lru/concurrent_lru_benchmark_test.go @@ -22,7 +22,7 @@ import ( const CacheSize = 50000 func Benchmark_LRU_AddIfAbsent(b *testing.B) { - c := NewLRUCacheMap(CacheSize) + c := NewLRUCacheMap(CacheSize, false) for a := 1; a <= CacheSize; a++ { val := new(int64) *val = int64(a) @@ -42,7 +42,7 @@ func Benchmark_LRU_AddIfAbsent(b *testing.B) { } func Benchmark_LRU_Add(b *testing.B) { - c := NewLRUCacheMap(CacheSize) + c := NewLRUCacheMap(CacheSize, false) for a := 1; a <= CacheSize; a++ { val := new(int64) *val = int64(a) @@ -59,7 +59,7 @@ func Benchmark_LRU_Add(b *testing.B) { } func Benchmark_LRU_Get(b *testing.B) { - c := NewLRUCacheMap(CacheSize) + c := NewLRUCacheMap(CacheSize, false) for a := 1; a <= CacheSize; a++ { val := new(int64) *val = int64(a) diff --git a/core/hotspot/cache/lru/concurrent_lru_test.go b/core/hotspot/cache/lru/concurrent_lru_test.go index aadcaf161..628fee6c9 100644 --- a/core/hotspot/cache/lru/concurrent_lru_test.go +++ b/core/hotspot/cache/lru/concurrent_lru_test.go @@ -23,7 +23,7 @@ import ( func Test_concurrentLruCounterCacheMap_Add_Get(t *testing.T) { t.Run("Test_concurrentLruCounterCacheMap_Add_Get", func(t *testing.T) { - c := NewLRUCacheMap(100) + c := NewLRUCacheMap(100, false) for i := 1; i <= 100; i++ { val := int64(i) c.Add(strconv.Itoa(i), &val) @@ -36,7 +36,7 @@ func Test_concurrentLruCounterCacheMap_Add_Get(t *testing.T) { func Test_concurrentLruCounterCacheMap_AddIfAbsent(t *testing.T) { t.Run("Test_concurrentLruCounterCacheMap_AddIfAbsent", func(t *testing.T) { - c := NewLRUCacheMap(100) + c := NewLRUCacheMap(100, false) for i := 1; i <= 99; i++ { val := int64(i) c.Add(strconv.Itoa(i), &val) @@ -52,7 +52,7 @@ func Test_concurrentLruCounterCacheMap_AddIfAbsent(t *testing.T) { func Test_concurrentLruCounterCacheMap_Contains(t *testing.T) { t.Run("Test_concurrentLruCounterCacheMap_Contains", func(t *testing.T) { - c := NewLRUCacheMap(100) + c := NewLRUCacheMap(100, false) for i := 1; i <= 100; i++ { val := int64(i) c.Add(strconv.Itoa(i), &val) @@ -69,7 +69,7 @@ func Test_concurrentLruCounterCacheMap_Contains(t *testing.T) { func Test_concurrentLruCounterCacheMap_Keys(t *testing.T) { t.Run("Test_concurrentLruCounterCacheMap_Add", func(t *testing.T) { - c := NewLRUCacheMap(100) + c := NewLRUCacheMap(100, false) for i := 1; i <= 100; i++ { val := int64(i) c.Add(strconv.Itoa(i), &val) @@ -82,7 +82,7 @@ func Test_concurrentLruCounterCacheMap_Keys(t *testing.T) { func Test_concurrentLruCounterCacheMap_Purge(t *testing.T) { t.Run("Test_concurrentLruCounterCacheMap_Add", func(t *testing.T) { - c := NewLRUCacheMap(100) + c := NewLRUCacheMap(100, false) for i := 1; i <= 100; i++ { val := int64(i) c.Add(strconv.Itoa(i), &val) @@ -95,7 +95,7 @@ func Test_concurrentLruCounterCacheMap_Purge(t *testing.T) { func Test_concurrentLruCounterCacheMap_Remove(t *testing.T) { t.Run("Test_concurrentLruCounterCacheMap_Add", func(t *testing.T) { - c := NewLRUCacheMap(100) + c := NewLRUCacheMap(100, false) for i := 1; i <= 100; i++ { val := int64(i) c.Add(strconv.Itoa(i), &val) From eb344902cc6a7e18ee89875ff3b9d97eaa71509c Mon Sep 17 00:00:00 2001 From: liqiangz Date: Wed, 30 Dec 2020 19:39:13 +0800 Subject: [PATCH 19/24] refine code --- core/hotspot/cache/wtinylfu/slru.go | 34 +++++++++++---------- core/hotspot/cache/wtinylfu/tinylfu.go | 8 ++--- core/hotspot/cache/wtinylfu/tinylfu_test.go | 6 ++-- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/core/hotspot/cache/wtinylfu/slru.go b/core/hotspot/cache/wtinylfu/slru.go index 097ff6e75..0e2effe9c 100644 --- a/core/hotspot/cache/wtinylfu/slru.go +++ b/core/hotspot/cache/wtinylfu/slru.go @@ -16,8 +16,10 @@ package wtinylfu import "container/list" +type listType int32 + const ( - admissionWindow uint8 = iota + admissionWindow listType = iota probationSegment protectedSegment ) @@ -25,10 +27,10 @@ const ( const protectedRatio = 0.8 type slruItem struct { - listId uint8 - key interface{} - value interface{} - keyHash uint64 + listType listType + key interface{} + value interface{} + keyHash uint64 } // slru is a segmented LRU. @@ -50,17 +52,17 @@ func newSLRU(cap int, data map[interface{}]*list.Element) *slru { } } -// Get looks up a key's value from the cache. -func (slru *slru) get(v *list.Element) { +// access access a value from the cache +func (slru *slru) access(v *list.Element) { item := v.Value.(*slruItem) - if item.listId == protectedSegment { + if item.listType == protectedSegment { slru.protectedLs.MoveToFront(v) return } if slru.protectedLs.Len() < slru.protectedCap { slru.probationLs.Remove(v) - item.listId = protectedSegment - slru.data[item.key] = slru.protectedLs.PushFront(item) + item.listType = protectedSegment + slru.protectedLs.PushFront(item) return } back := slru.protectedLs.Back() @@ -68,8 +70,8 @@ func (slru *slru) get(v *list.Element) { // swap the two item *backItem, *item = *item, *backItem - backItem.listId = protectedSegment - item.listId = probationSegment + backItem.listType = protectedSegment + item.listType = probationSegment slru.data[item.key] = v slru.data[backItem.key] = back @@ -80,7 +82,7 @@ func (slru *slru) get(v *list.Element) { // add set a value in the cache func (slru *slru) add(newItem slruItem) { - newItem.listId = probationSegment + newItem.listType = probationSegment if slru.probationLs.Len() < slru.probationCap || slru.Len() < slru.probationCap+slru.protectedCap { slru.data[newItem.key] = slru.probationLs.PushFront(&newItem) return @@ -113,7 +115,7 @@ func (slru *slru) Remove(key interface{}) (interface{}, bool) { return nil, false } item := v.Value.(*slruItem) - if item.listId == protectedSegment { + if item.listType == protectedSegment { slru.protectedLs.Remove(v) } else { slru.probationLs.Remove(v) @@ -142,8 +144,8 @@ func newLRU(cap int, data map[interface{}]*list.Element) *lru { } } -// Get returns a value from the cache -func (lru *lru) get(v *list.Element) { +// access access a value from the cache +func (lru *lru) access(v *list.Element) { lru.evictList.MoveToFront(v) } diff --git a/core/hotspot/cache/wtinylfu/tinylfu.go b/core/hotspot/cache/wtinylfu/tinylfu.go index 81c86d778..00f6ebef6 100644 --- a/core/hotspot/cache/wtinylfu/tinylfu.go +++ b/core/hotspot/cache/wtinylfu/tinylfu.go @@ -107,10 +107,10 @@ func (t *TinyLfu) get(key interface{}, isInternal bool) (interface{}, bool) { } v := item.value - if item.listId == admissionWindow { - t.lru.get(val) + if item.listType == admissionWindow { + t.lru.access(val) } else { - t.slru.get(val) + t.slru.access(val) } if !isInternal && t.stats != nil { t.stats.RecordHits() @@ -177,7 +177,7 @@ func (t *TinyLfu) Remove(key interface{}) (isFound bool) { } item := val.Value.(*slruItem) - if item.listId == admissionWindow { + if item.listType == admissionWindow { t.lru.Remove(key) return true } else { diff --git a/core/hotspot/cache/wtinylfu/tinylfu_test.go b/core/hotspot/cache/wtinylfu/tinylfu_test.go index 64f14610c..38087525e 100644 --- a/core/hotspot/cache/wtinylfu/tinylfu_test.go +++ b/core/hotspot/cache/wtinylfu/tinylfu_test.go @@ -36,13 +36,13 @@ func (t *tinyLFUTest) assertLen(admission, protected, probation int) { assert.True(t.t, sz == admission+protected+probation && tz == protected && bz == probation) } -func (t *tinyLFUTest) assertLRUValue(k int, id uint8) { +func (t *tinyLFUTest) assertLRUValue(k int, id listType) { v := t.lfu.items[k].Value.(*slruItem).value assert.True(t.t, v != nil) ak := k av := v - listId := t.lfu.items[k].Value.(*slruItem).listId - assert.True(t.t, ak == av && listId == id) + listType := t.lfu.items[k].Value.(*slruItem).listType + assert.True(t.t, ak == av && listType == id) } func TestTinyLFU(t *testing.T) { From e2c3f0d216577bd611b05d3fea1ea7708e8bf4b4 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Wed, 30 Dec 2020 19:43:42 +0800 Subject: [PATCH 20/24] refine code --- core/hotspot/cache/wtinylfu/slru.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hotspot/cache/wtinylfu/slru.go b/core/hotspot/cache/wtinylfu/slru.go index 0e2effe9c..6b1527d88 100644 --- a/core/hotspot/cache/wtinylfu/slru.go +++ b/core/hotspot/cache/wtinylfu/slru.go @@ -16,7 +16,7 @@ package wtinylfu import "container/list" -type listType int32 +type listType uint8 const ( admissionWindow listType = iota From 994cc6cd5ac29b112c83938519e04cd48b536627 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Wed, 30 Dec 2020 19:49:10 +0800 Subject: [PATCH 21/24] refine code --- core/hotspot/cache/wtinylfu/slru.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hotspot/cache/wtinylfu/slru.go b/core/hotspot/cache/wtinylfu/slru.go index 6b1527d88..0b98ccf94 100644 --- a/core/hotspot/cache/wtinylfu/slru.go +++ b/core/hotspot/cache/wtinylfu/slru.go @@ -62,7 +62,7 @@ func (slru *slru) access(v *list.Element) { if slru.protectedLs.Len() < slru.protectedCap { slru.probationLs.Remove(v) item.listType = protectedSegment - slru.protectedLs.PushFront(item) + slru.data[item.key] = slru.protectedLs.PushFront(item) return } back := slru.protectedLs.Back() From 2789e797397808117d41cb9759b15aca289045d0 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Mon, 4 Jan 2021 10:45:06 +0800 Subject: [PATCH 22/24] refine --- core/hotspot/cache/wtinylfu/hash.go | 9 +++++---- core/hotspot/cache/wtinylfu/hash_benchmark_test.go | 4 +++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/core/hotspot/cache/wtinylfu/hash.go b/core/hotspot/cache/wtinylfu/hash.go index 2f19f371c..16839b09b 100644 --- a/core/hotspot/cache/wtinylfu/hash.go +++ b/core/hotspot/cache/wtinylfu/hash.go @@ -23,7 +23,8 @@ import ( ) var ( - fnv64 = fnv.New64() + fnv64 = fnv.New64() + byteSum = make([]byte, 0, 8) ) func sum(k interface{}) uint64 { @@ -65,7 +66,7 @@ func sum(k interface{}) uint64 { if h, ok := hashPointer(k); ok { return h } - if h, ok := hashWithSprintf(k); ok { + if h, ok := hashOtherWithSprintf(k); ok { return h } return 0 @@ -81,7 +82,7 @@ func hashString(data string) uint64 { return hashByteArray([]byte(data)) } -func hashWithSprintf(data interface{}) (uint64, bool) { +func hashOtherWithSprintf(data interface{}) (uint64, bool) { v := fmt.Sprintf("%v", data) return hashString(v), true } @@ -91,7 +92,7 @@ func hashByteArray(bytes []byte) uint64 { if err != nil { return 0 } - hash := binary.LittleEndian.Uint64(fnv64.Sum(nil)) + hash := binary.LittleEndian.Uint64(fnv64.Sum(byteSum)) fnv64.Reset() return hash } diff --git a/core/hotspot/cache/wtinylfu/hash_benchmark_test.go b/core/hotspot/cache/wtinylfu/hash_benchmark_test.go index f40c83dbd..0f37ef7c3 100644 --- a/core/hotspot/cache/wtinylfu/hash_benchmark_test.go +++ b/core/hotspot/cache/wtinylfu/hash_benchmark_test.go @@ -22,7 +22,9 @@ func Benchmark_Hash_Num(b *testing.B) { num := 100020000 b.ResetTimer() for i := 0; i < b.N; i++ { - sum(num) + for j := 1; j < 10000; j++ { + sum(num) + } } } func Benchmark_Hash_String(b *testing.B) { From d8912bcd6425c9977d5362de8d4256ddc6060010 Mon Sep 17 00:00:00 2001 From: liqiangz Date: Mon, 4 Jan 2021 17:03:45 +0800 Subject: [PATCH 23/24] refine --- core/hotspot/cache/wtinylfu/hash_benchmark_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hotspot/cache/wtinylfu/hash_benchmark_test.go b/core/hotspot/cache/wtinylfu/hash_benchmark_test.go index 0f37ef7c3..0a7ba35c6 100644 --- a/core/hotspot/cache/wtinylfu/hash_benchmark_test.go +++ b/core/hotspot/cache/wtinylfu/hash_benchmark_test.go @@ -43,7 +43,7 @@ func Benchmark_Hash_Pointer(b *testing.B) { sum(pointer) } } -func Benchmark_Hash_WithSprintf(b *testing.B) { +func Benchmark_Hash_OtherWithSprintf(b *testing.B) { type test struct { test1 uint32 test2 string From c1ede579244ceb2e5e0c6d0b6a8f0775017720cd Mon Sep 17 00:00:00 2001 From: liqiangz Date: Mon, 4 Jan 2021 17:04:17 +0800 Subject: [PATCH 24/24] refine --- core/hotspot/cache/wtinylfu/hash_benchmark_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/hotspot/cache/wtinylfu/hash_benchmark_test.go b/core/hotspot/cache/wtinylfu/hash_benchmark_test.go index 0a7ba35c6..b61e4f231 100644 --- a/core/hotspot/cache/wtinylfu/hash_benchmark_test.go +++ b/core/hotspot/cache/wtinylfu/hash_benchmark_test.go @@ -22,9 +22,7 @@ func Benchmark_Hash_Num(b *testing.B) { num := 100020000 b.ResetTimer() for i := 0; i < b.N; i++ { - for j := 1; j < 10000; j++ { - sum(num) - } + sum(num) } } func Benchmark_Hash_String(b *testing.B) {