From a2d66433f605fa25be92d716c7d9849cfdbe0ae9 Mon Sep 17 00:00:00 2001 From: maypok86 Date: Mon, 11 Dec 2023 21:15:45 +0300 Subject: [PATCH] [#17] Improve S3-FIFO --- internal/s3fifo/main.go | 12 ++++++++++ internal/s3fifo/policy.go | 26 +++++++++------------- internal/s3fifo/small.go | 46 +++++++++++++++++++-------------------- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/internal/s3fifo/main.go b/internal/s3fifo/main.go index ca5a94d..50484a6 100644 --- a/internal/s3fifo/main.go +++ b/internal/s3fifo/main.go @@ -6,6 +6,8 @@ import ( "github.com/maypok86/otter/internal/node" ) +const maxReinsertions = 20 + type main[K comparable, V any] struct { q *deque.Deque[*node.Node[K, V]] cost uint32 @@ -26,6 +28,7 @@ func (m *main[K, V]) insert(n *node.Node[K, V]) { } func (m *main[K, V]) evict(deleted []*node.Node[K, V]) []*node.Node[K, V] { + reinsertions := 0 for m.cost > 0 { n := m.q.PopFront() if n.Meta.IsDeleted() { @@ -41,6 +44,15 @@ func (m *main[K, V]) evict(deleted []*node.Node[K, V]) []*node.Node[K, V] { return append(deleted, n) } + // to avoid the worst case O(n), we remove the 20th reinserted consecutive element. + reinsertions++ + if reinsertions >= maxReinsertions { + n.Meta = n.Meta.UnmarkMain().MarkDeleted() + m.cost -= n.Cost() + // can remove + return append(deleted, n) + } + m.q.PushBack(n) n.Meta = n.Meta.DecrementFrequency() } diff --git a/internal/s3fifo/policy.go b/internal/s3fifo/policy.go index e991876..74530e6 100644 --- a/internal/s3fifo/policy.go +++ b/internal/s3fifo/policy.go @@ -52,44 +52,38 @@ func (p *Policy[K, V]) read(deleted []*node.Node[K, V], n *node.Node[K, V]) []*n } func (p *Policy[K, V]) insert(deleted []*node.Node[K, V], n *node.Node[K, V]) []*node.Node[K, V] { - for p.isFull() { - deleted = p.evict(deleted) - } - if n.Meta.IsDeleted() { return deleted } if p.ghost.isGhost(n) { - if !n.Meta.IsMain() { - p.main.insert(n) - } - return deleted + p.main.insert(n) + } else { + p.small.insert(n) } - if !n.Meta.IsSmall() { - p.small.insert(n) + for p.isFull() { + deleted = p.evict(deleted) } return deleted } func (p *Policy[K, V]) update(deleted []*node.Node[K, V], n *node.Node[K, V], costDiff uint32) []*node.Node[K, V] { - for p.isFull() { - deleted = p.evict(deleted) - } - if n.Meta.IsDeleted() { return deleted } if n.Meta.IsSmall() { p.small.cost += costDiff - } - if n.Meta.IsMain() { + } else if n.Meta.IsMain() { p.main.cost += costDiff } + for p.isFull() { + deleted = p.evict(deleted) + } + return deleted } diff --git a/internal/s3fifo/small.go b/internal/s3fifo/small.go index 2625aa2..91578dd 100644 --- a/internal/s3fifo/small.go +++ b/internal/s3fifo/small.go @@ -34,34 +34,32 @@ func (s *small[K, V]) insert(n *node.Node[K, V]) { } func (s *small[K, V]) evict(deleted []*node.Node[K, V]) []*node.Node[K, V] { - for s.cost > 0 { - n := s.q.PopFront() - s.cost -= n.Cost() - n.Meta = n.Meta.UnmarkSmall() - if n.Meta.IsDeleted() { - return deleted - } - if n.IsExpired() { - n.Meta = n.Meta.MarkDeleted() - // can remove - return append(deleted, n) - } + if s.cost == 0 { + return deleted + } - if n.Meta.GetFrequency() > 1 { - if !n.Meta.IsMain() { - s.main.insert(n) - for s.main.isFull() { - deleted = s.main.evict(deleted) - } - n.Meta = n.Meta.ResetFrequency() - continue - } - } + n := s.q.PopFront() + s.cost -= n.Cost() + n.Meta = n.Meta.UnmarkSmall() + if n.Meta.IsDeleted() { + return deleted + } + if n.IsExpired() { + n.Meta = n.Meta.MarkDeleted() + // can remove + return append(deleted, n) + } - return s.ghost.insert(deleted, n) + if n.Meta.GetFrequency() > 1 { + s.main.insert(n) + for s.main.isFull() { + deleted = s.main.evict(deleted) + } + n.Meta = n.Meta.ResetFrequency() + return deleted } - return deleted + return s.ghost.insert(deleted, n) } func (s *small[K, V]) length() int {