Skip to content

Commit

Permalink
fix lru-k
Browse files Browse the repository at this point in the history
  • Loading branch information
JimChenWYU committed Aug 27, 2024
1 parent 5e49343 commit e9d1155
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 225 deletions.
18 changes: 18 additions & 0 deletions internal/list/fifo.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ func (l *FIFO[K, V]) Init() *FIFO[K, V] {
return l
}

func (l *FIFO[K, V]) Contains(key K) bool {
_, ok := l.items[key]
return ok
}

// Get returns the element with the given key from the list.
func (l *FIFO[K, V]) Get(key K) (e *Entry[K, V], ok bool) {
e, ok = l.items[key]
Expand Down Expand Up @@ -108,3 +113,16 @@ func (l *FIFO[K, V]) delete(e *Entry[K, V]) {
e.prev = nil // avoid memory leaks
l.len--
}

// Resize changes the list size.
func (c *FIFO[K, V]) Resize(size int) (evicted int) {
diff := c.Len() - size
if diff < 0 {
diff = 0
}
for i := 0; i < diff; i++ {
c.Pop()
}
c.size = size
return diff
}
19 changes: 19 additions & 0 deletions internal/list/fifo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ func TestFIFO(t *testing.T) {
list.lazyInit()
}

func TestFIFO_Resize(t *testing.T) {
list := NewFIFOList[int, int](10)
assert.EqualValues(t, 10, list.Size())
assert.EqualValues(t, 0, list.Len())
assert.EqualValues(t, 0, list.Resize(4))
assert.EqualValues(t, 4, list.Size())

list.Push(1, 1)
list.Push(2, 2)
list.Push(3, 3)
list.Push(4, 4)

assert.True(t, list.Contains(1))
assert.EqualValues(t, 1, list.Resize(3))
assert.EqualValues(t, 3, list.Size())
assert.EqualValues(t, 3, list.Len())
assert.False(t, list.Contains(1))
}

func TestFIFO_Push(t *testing.T) {
list := NewFIFOList[int, int](3)
list.Push(1, 1)
Expand Down
93 changes: 46 additions & 47 deletions lru.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package lru

import (
"slices"
"sync"

"github.com/lovelysunlight/lru-go/internal/list"
Expand Down Expand Up @@ -60,7 +59,7 @@ func (c *Cache[K, V]) Get(key K) (value V, ok bool) {
if value, ok = c.lru.Get(key); ok {
return c.OptionalCopyValue(value), ok
}
if c.IsUpgradeToLRUK() {
if c.IsEnableLRUK() {
if value, ok = c.visit.Get(key); ok {
visits, _ := c.visit.PeekVisits(key)
if c.isExpectedVisits(visits) {
Expand All @@ -69,7 +68,7 @@ func (c *Cache[K, V]) Get(key K) (value V, ok bool) {
return c.OptionalCopyValue(value), true
}
}
} else if c.IsUpgradeTo2Q() {
} else if c.IsEnable2Q() {
if e, ok := c.fifo.Remove(key); ok {
c.lru.Put(key, e.Value)
return c.OptionalCopyValue(e.Value), true
Expand All @@ -93,7 +92,7 @@ func (c *Cache[K, V]) Peek(key K) (value V, ok bool) {
if value, ok = c.lru.Peek(key); ok {
return c.OptionalCopyValue(value), ok
}
if c.IsUpgradeToLRUK() {
if c.IsEnableLRUK() {
if value, ok = c.visit.Peek(key); ok {
return c.OptionalCopyValue(value), ok
}
Expand All @@ -111,7 +110,7 @@ func (c *Cache[K, V]) Contains(key K) (ok bool) {
c.mux.RLock()
defer c.mux.RUnlock()

return c.lru.Contains(key) || c.visit.Contains(key)
return c.lru.Contains(key)
}

// Returns the oldest entry without updating the "recently used"-ness of the key.
Expand Down Expand Up @@ -146,9 +145,6 @@ func (c *Cache[K, V]) Remove(key K) (value V, ok bool) {
if value, ok = c.lru.Remove(key); ok {
return value, true
}
if c.IsUpgradeToLRUK() {
return c.visit.Remove(key)
}
return
}

Expand All @@ -163,18 +159,14 @@ func (c *Cache[K, V]) Push(key K, value V) (oldKey K, oldValue V, ok bool) {
defer c.mux.Unlock()

if !c.lru.Contains(key) {
if c.IsUpgradeToLRUK() {
oldKey, oldValue, ok = c.visit.Push(key, value)
visits, _ := c.visit.PeekVisits(key)
if c.isExpectedVisits(visits) {
c.moveToLru(key)
}
if c.IsEnableLRUK() {
c.visit.Push(key, value)
return
} else if c.IsUpgradeTo2Q() {
if _, ok := c.fifo.Get(key); !ok {
} else if c.IsEnable2Q() {
if !c.fifo.Contains(key) {
c.fifo.Push(key, value)
}
return oldKey, oldValue, false
return
}
}

Expand All @@ -192,14 +184,10 @@ func (c *Cache[K, V]) Put(key K, value V) (oldValue V, ok bool) {
defer c.mux.Unlock()

if !c.lru.Contains(key) {
if c.IsUpgradeToLRUK() {
oldValue, ok = c.visit.Put(key, value)
visits, _ := c.visit.PeekVisits(key)
if c.isExpectedVisits(visits) {
c.moveToLru(key)
}
if c.IsEnableLRUK() {
c.visit.Put(key, value)
return
} else if c.IsUpgradeTo2Q() {
} else if c.IsEnable2Q() {
if _, ok := c.fifo.Get(key); !ok {
c.fifo.Push(key, value)
}
Expand Down Expand Up @@ -231,9 +219,7 @@ func (c *Cache[K, V]) Keys() []K {
c.mux.RLock()
defer c.mux.RUnlock()

return c.OptionalCopyKeyN(
slices.Concat(c.lru.Keys(), c.visit.Keys()),
)
return c.OptionalCopyKeyN(c.lru.Keys())
}

// Values returns a slice of the values in the cache, from oldest to newest.
Expand All @@ -246,9 +232,7 @@ func (c *Cache[K, V]) Values() []V {
c.mux.RLock()
defer c.mux.RUnlock()

return c.OptionalCopyValueN(
slices.Concat(c.lru.Values(), c.visit.Values()),
)
return c.OptionalCopyValueN(c.lru.Values())
}

// Clears all cache entries.
Expand All @@ -261,9 +245,6 @@ func (c *Cache[K, V]) Values() []V {
func (c *Cache[K, V]) Clear() {
c.mux.Lock()
c.lru.Clear()
if c.IsUpgradeToLRUK() {
c.visit.Clear()
}
c.mux.Unlock()
}

Expand All @@ -283,50 +264,68 @@ func (c *Cache[K, V]) Resize(size int) (evicted int) {

var _ simplelru.LRUCache[any, any] = (*Cache[any, any])(nil)

// VisitCacheCap returns the size of LFU cache.
func (c *Cache[K, V]) VisitCacheCap() int {
// VisitsCap returns the size of LFU cache.
func (c *Cache[K, V]) VisitsCap() int {
c.mux.RLock()
defer c.mux.RUnlock()

return c.visit.Cap()
}

// VisitCacheLen returns the number of items in the LFU cache.
func (c *Cache[K, V]) VisitCacheLen() int {
// VisitsLen returns the number of items in the LFU cache.
func (c *Cache[K, V]) VisitsLen() int {
c.mux.RLock()
defer c.mux.RUnlock()

return c.visit.Len()
}

// Resize changes the LFU cache size.
func (c *Cache[K, V]) VisitCacheResize(size int) int {
// VisitsResize changes the LFU cache size.
func (c *Cache[K, V]) VisitsResize(size int) int {
c.mux.Lock()
defer c.mux.Unlock()

return c.visit.Resize(size)
}

// FIFOCap returns the size of fifo-list.
func (c *Cache[K, V]) FIFOCap() int {
c.mux.RLock()
defer c.mux.RUnlock()

return c.fifo.Size()
}

// FIFOLen returns the number of items in the fifo-list
func (c *Cache[K, V]) FIFOLen() int {
c.mux.RLock()
defer c.mux.RUnlock()

return c.fifo.Len()
}

// FIFOResize changes the fifo-list size.
func (c *Cache[K, V]) FIFOResize(size int) int {
c.mux.Lock()
defer c.mux.Unlock()

return c.fifo.Resize(size)
}

// whether or not enable LRU-K algorithm
func (c *Cache[K, V]) IsUpgradeToLRUK() bool {
func (c *Cache[K, V]) IsEnableLRUK() bool {
return c.visitThreshold > 1
}

// whether or not enable 2Q algorithm
func (c *Cache[K, V]) IsUpgradeTo2Q() bool {
func (c *Cache[K, V]) IsEnable2Q() bool {
return c.fifo != nil && c.fifo.Size() > 0
}

func (c *Cache[K, V]) isExpectedVisits(visits uint64) bool {
return visits >= c.visitThreshold
}

func (c *Cache[K, V]) moveToLru(key K) {
if value, ok := c.visit.Remove(key); ok {
c.lru.Push(key, value)
}
}

type cacheOptionFunc[K comparable, V any] func(*Cache[K, V])

// Enable to return a deep copy of the value in `Get`, `Peek`, `PeekOldest`, `Keys` and `Values`.
Expand Down
16 changes: 12 additions & 4 deletions lru_deepcopy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,7 @@ func TestCacheUpgradeToLRUK_enable_deepcopy_Values(t *testing.T) {
cache.Put("a", map[string]string{
"a": "a",
})
cache.Get("a")

v := cache.Values()
assert.EqualValues(t, []map[string]string{
Expand All @@ -617,7 +618,7 @@ func TestCacheUpgradeToLRUK_enable_deepcopy_Values(t *testing.T) {
t.Run("slice", func(t *testing.T) {
cache, _ := New[string, []int](2, EnableDeepCopy[string, []int](), EnableLRUK[string, []int](2))
cache.Put("a", []int{1, 2, 3})

cache.Get("a")
v := cache.Values()
assert.EqualValues(t, [][]int{{1, 2, 3}}, v)
v[0][0] = 4
Expand All @@ -631,7 +632,7 @@ func TestCacheUpgradeToLRUK_enable_deepcopy_Values(t *testing.T) {
}
cache, _ := New[string, *TestCase](2, EnableDeepCopy[string, *TestCase](), EnableLRUK[string, *TestCase](2))
cache.Put("a", &TestCase{Name: "a"})

cache.Get("a")
v := cache.Values()
assert.EqualValues(t, []*TestCase{{Name: "a"}}, v)
v[0].Name = "b"
Expand All @@ -647,6 +648,7 @@ func TestCacheUpgradeToLRUK_disable_deepcopy_Values(t *testing.T) {
cache.Put("a", map[string]string{
"a": "a",
})
cache.Get("a")

v := cache.Values()
assert.EqualValues(t, []map[string]string{
Expand All @@ -668,6 +670,7 @@ func TestCacheUpgradeToLRUK_disable_deepcopy_Values(t *testing.T) {
t.Run("slice", func(t *testing.T) {
cache, _ := New[string, []int](2, DisableDeepCopy[string, []int](), EnableLRUK[string, []int](2))
cache.Put("a", []int{1, 2, 3})
cache.Get("a")

v := cache.Values()
assert.EqualValues(t, [][]int{{1, 2, 3}}, v)
Expand All @@ -686,6 +689,7 @@ func TestCacheUpgradeToLRUK_disable_deepcopy_Values(t *testing.T) {
}
cache, _ := New[string, *TestCase](2, DisableDeepCopy[string, *TestCase](), EnableLRUK[string, *TestCase](2))
cache.Put("a", &TestCase{Name: "a"})
cache.Get("a")

v := cache.Values()
assert.EqualValues(t, []*TestCase{{Name: "a"}}, v)
Expand All @@ -706,7 +710,9 @@ func TestCacheUpgradeToLRUK_enable_deepcopy_Keys(t *testing.T) {
Name string
}
cache, _ := New[*TestCase, *TestCase](2, EnableDeepCopy[*TestCase, *TestCase](), EnableLRUK[*TestCase, *TestCase](2))
cache.Put(&TestCase{Name: "b"}, &TestCase{Name: "a"})
key := &TestCase{Name: "b"}
cache.Put(key, &TestCase{Name: "a"})
cache.Get(key)

v := cache.Keys()
assert.EqualValues(t, []*TestCase{{Name: "b"}}, v)
Expand All @@ -723,7 +729,9 @@ func TestCacheUpgradeToLRUK_disable_deepcopy_Keys(t *testing.T) {
Name string
}
cache, _ := New[*TestCase, *TestCase](2, DisableDeepCopy[*TestCase, *TestCase](), EnableLRUK[*TestCase, *TestCase](2))
cache.Put(&TestCase{Name: "b"}, &TestCase{Name: "a"})
key := &TestCase{Name: "b"}
cache.Put(key, &TestCase{Name: "a"})
cache.Get(key)

v := cache.Keys()
assert.EqualValues(t, []*TestCase{{Name: "b"}}, v)
Expand Down
Loading

0 comments on commit e9d1155

Please sign in to comment.