Skip to content

Commit

Permalink
Add another test, clean up add logic a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
nakkamarra committed Jul 25, 2024
1 parent 72c21e5 commit 0907313
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 12 deletions.
23 changes: 11 additions & 12 deletions clock/clock.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,18 @@ func (c *Cache[K, V]) Add(key K, val V) {
value: val,
})
c.indices[key] = len(c.buf) - 1
c.clockhand = (c.clockhand + 1) % len(c.buf)
return
}
// Full, evict by reference bit then replace
hand := c.clockhand
for c.touches[hand].Load() > 0 {
c.touches[hand].Add(-1)
hand = (hand + 1) % len(c.buf)
} else {
// Full, evict by reference bit then replace
for c.touches[c.clockhand].Load() > 0 {
c.touches[c.clockhand].Add(-1)
c.clockhand += 1 % len(c.buf)
}
c.Evict(c.buf[c.clockhand].key)
c.buf.insertAt(c.clockhand, key, val)
c.indices[key] = c.clockhand
c.touches[c.clockhand].Add(1)
}
c.Evict(c.buf[hand].key)
c.buf.insertAt(hand, key, val)
c.indices[key] = hand
c.clockhand = hand
c.clockhand = (c.clockhand + 1) % len(c.buf)
}

// Get returns the value for a given key, if present.
Expand Down
93 changes: 93 additions & 0 deletions clock/clock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,96 @@ func TestCacheGet(t *testing.T) {
})
}
}

func TestCacheAdd(t *testing.T) {
tests := []struct {
name string
size int
keysToAdd []string
valueToAdd []string
expectedCache buffer[string, string]
}{
{
"beyond_capacity_evicts_first_untouched",
3,
[]string{"key-a", "key-b", "key-c", "key-d", "key-e"},
[]string{"val-a", "val-b", "val-c", "val-d", "val-e"},
buffer[string, string]{
&bufferItem[string, string]{key: "key-e", value: "val-e"},
&bufferItem[string, string]{key: "key-b", value: "val-b"},
&bufferItem[string, string]{key: "key-d", value: "val-d"},
},
},
{
"multiple_touches_decreases_eviction_chances",
3,
[]string{"key-a", "key-b", "key-c", "key-a", "key-a", "key-d", "key-e"},
[]string{"val-a", "val-b", "val-c", "val-a", "val-a", "val-d", "val-e"},
buffer[string, string]{
&bufferItem[string, string]{key: "key-a", value: "val-a"},
&bufferItem[string, string]{key: "key-e", value: "val-e"},
&bufferItem[string, string]{key: "key-d", value: "val-d"},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cache := New[string, string](test.size)
for i := 0; i < len(test.keysToAdd); i++ {
key := test.keysToAdd[i]
val := test.valueToAdd[i]
cache.Add(key, val)
}
for i := 0; i < len(cache.buf); i++ {
cachedKey := cache.buf[i].key
cachedValue := cache.buf[i].value
expectedKey := test.expectedCache[i].key
expectedValue := test.expectedCache[i].value
if cachedKey != expectedKey {
t.Fatalf("bad cache key; got %s, wanted %s at index %d", cachedKey, expectedKey, i)
}
if cachedValue != expectedValue {
t.Fatalf("bad cache value; got %s, wanted %s at index %d", cachedValue, expectedValue, i)
}
}
})
}
}

func BenchmarkGetAllHits(b *testing.B) {
b.ReportAllocs()
type complexStruct struct {
a, b, c, d, e, f int64
k, l, m, n, o, p float64
}
// Populate the cache
l := New[int, complexStruct](32)
for z := 0; z < 32; z++ {
l.Add(z, complexStruct{a: int64(z)})
}

b.ResetTimer()
for z := 0; z < b.N; z++ {
// take the lower 5 bits as mod 32 so we always hit
l.Get(z & 31)
}
}

func BenchmarkGetHalfHits(b *testing.B) {
b.ReportAllocs()
type complexStruct struct {
a, b, c, d, e, f int64
k, l, m, n, o, p float64
}
// Populate the cache
l := New[int, complexStruct](32)
for z := 0; z < 32; z++ {
l.Add(z, complexStruct{a: int64(z)})
}

b.ResetTimer()
for z := 0; z < b.N; z++ {
// take the lower 4 bits as mod 16 shifted left by 1 to
l.Get((z&15)<<1 | z&16>>4 | z&1<<4)
}
}

0 comments on commit 0907313

Please sign in to comment.