Skip to content

Commit

Permalink
add a Resize method to 2Q
Browse files Browse the repository at this point in the history
  • Loading branch information
veloting authored and mgaffney committed Sep 21, 2023
1 parent d46c1d9 commit d851586
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 2 deletions.
36 changes: 34 additions & 2 deletions 2q.go
Expand Up @@ -30,8 +30,10 @@ const (
// head. The ARCCache is similar, but does not require setting any
// parameters.
type TwoQueueCache[K comparable, V any] struct {
size int
recentSize int
size int
recentSize int
recentRatio float64
ghostRatio float64

recent simplelru.LRUCache[K, V]
frequent simplelru.LRUCache[K, V]
Expand Down Expand Up @@ -80,6 +82,8 @@ func New2QParams[K comparable, V any](size int, recentRatio, ghostRatio float64)
c := &TwoQueueCache[K, V]{
size: size,
recentSize: recentSize,
recentRatio: recentRatio,
ghostRatio: ghostRatio,
recent: recent,
frequent: frequent,
recentEvict: recentEvict,
Expand Down Expand Up @@ -171,6 +175,34 @@ func (c *TwoQueueCache[K, V]) Len() int {
return c.recent.Len() + c.frequent.Len()
}

// Resize changes the cache size.
func (c *TwoQueueCache[K, V]) Resize(size int) (evicted int) {
c.lock.Lock()
defer c.lock.Unlock()

// Recalculate the sub-sizes
recentSize := int(float64(size) * c.recentRatio)
evictSize := int(float64(size) * c.ghostRatio)
c.size = size
c.recentSize = recentSize

// ensureSpace
diff := c.recent.Len() + c.frequent.Len() - size
if diff < 0 {
diff = 0
}
for i := 0; i < diff; i++ {
c.ensureSpace(true)
}

// Reallocate the LRUs
c.recent.Resize(size)
c.frequent.Resize(size)
c.recentEvict.Resize(evictSize)

return diff
}

// Keys returns a slice of the keys in the cache.
// The frequently used keys are first in the returned slice.
func (c *TwoQueueCache[K, V]) Keys() []K {
Expand Down
69 changes: 69 additions & 0 deletions 2q_test.go
Expand Up @@ -218,6 +218,75 @@ func Test2Q_Add_RecentEvict(t *testing.T) {
}
}

func Test2Q_Resize(t *testing.T) {
l, err := New2Q[int, int](100)
if err != nil {
t.Fatalf("err: %v", err)
}

// Touch all the entries, should be in t1
for i := 0; i < 100; i++ {
l.Add(i, i)
}

evicted := l.Resize(50)
if evicted != 50 {
t.Fatalf("bad: %d", evicted)
}

if n := l.recent.Len(); n != 50 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}

l, err = New2Q[int, int](100)
if err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < 100; i++ {
l.Add(i, i)
}

for i := 0; i < 50; i++ {
l.Add(i, i)
}

evicted = l.Resize(50)
if evicted != 50 {
t.Fatalf("bad: %d", evicted)
}

if n := l.recent.Len(); n != 12 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 38 {
t.Fatalf("bad: %d", n)
}

l, err = New2Q[int, int](100)
if err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < 100; i++ {
l.Add(i, i)
l.Add(i, i)
}

evicted = l.Resize(50)
if evicted != 50 {
t.Fatalf("bad: %d", evicted)
}

if n := l.recent.Len(); n != 0 {
t.Fatalf("bad: %d", n)
}
if n := l.frequent.Len(); n != 50 {
t.Fatalf("bad: %d", n)
}
}

func Test2Q(t *testing.T) {
l, err := New2Q[int, int](128)
if err != nil {
Expand Down

0 comments on commit d851586

Please sign in to comment.