Skip to content

Commit

Permalink
[Chore] Reduce allocations
Browse files Browse the repository at this point in the history
  • Loading branch information
maypok86 committed Sep 26, 2023
1 parent 4484297 commit 75407e8
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 14 deletions.
1 change: 1 addition & 0 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func New[K comparable, V any](opts ...Option) (*Cache[K, V], error) {

c.policy = s3fifo.NewPolicy[K, V](o.capacity, func(n *node.Node[K, V]) {
c.getShard(n.Key()).EvictNode(n)
node.Free(n)
})
if o.statsEnabled {
c.stats = stats.New()
Expand Down
47 changes: 33 additions & 14 deletions internal/node/node.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package node

import (
"sync"
"sync/atomic"

"github.com/maypok86/otter/internal/unixtime"
Expand All @@ -12,19 +13,37 @@ const (
defaultFrequency = int32(0)
)

var nodePool sync.Pool

type Node[K comparable, V any] struct {
key K
value V
expiration uint64
frequency atomic.Int32
frequency int32
}

func New[K comparable, V any](key K, value V, expiration uint64) *Node[K, V] {
return &Node[K, V]{
key: key,
value: value,
expiration: expiration,
n := nodePool.Get()
if n == nil {
return &Node[K, V]{
key: key,
value: value,
expiration: expiration,
}
}

//nolint:errcheck // we check nil value (not found in pool or cast error)
v := n.(*Node[K, V])
v.key = key
v.value = value
v.expiration = expiration

return v
}

func Free[K comparable, V any](n *Node[K, V]) {
n.frequency = defaultFrequency
nodePool.Put(n)
}

func (n *Node[K, V]) Key() K {
Expand All @@ -40,29 +59,29 @@ func (n *Node[K, V]) IsExpired() bool {
}

func (n *Node[K, V]) IsGhost() bool {
return n.frequency.Load() == ghostFrequency
return atomic.LoadInt32(&n.frequency) == ghostFrequency
}

func (n *Node[K, V]) Touch() bool {
for {
frequency := n.frequency.Load()
frequency := atomic.LoadInt32(&n.frequency)
if frequency >= maxFrequency {
// don't need cas
return true
}
if n.frequency.CompareAndSwap(frequency, minInt32(frequency+1, maxFrequency)) {
if atomic.CompareAndSwapInt32(&n.frequency, frequency, minInt32(frequency+1, maxFrequency)) {
return frequency != ghostFrequency // is not ghost
}
}
}

func (n *Node[K, V]) Untouch() bool {
for {
frequency := n.frequency.Load()
frequency := atomic.LoadInt32(&n.frequency)

// touched
if frequency > defaultFrequency {
if n.frequency.CompareAndSwap(frequency, frequency-1) {
if atomic.CompareAndSwapInt32(&n.frequency, frequency, frequency-1) {
return true
}
continue
Expand All @@ -73,19 +92,19 @@ func (n *Node[K, V]) Untouch() bool {
}

func (n *Node[K, V]) BecomeGhost() bool {
return n.frequency.CompareAndSwap(defaultFrequency, ghostFrequency)
return atomic.CompareAndSwapInt32(&n.frequency, defaultFrequency, ghostFrequency)
}

func (n *Node[K, V]) MustGhost() {
n.frequency.Store(ghostFrequency)
atomic.StoreInt32(&n.frequency, ghostFrequency)
}

func (n *Node[K, V]) Revive() bool {
return n.frequency.CompareAndSwap(ghostFrequency, defaultFrequency)
return atomic.CompareAndSwapInt32(&n.frequency, ghostFrequency, defaultFrequency)
}

func (n *Node[K, V]) Touched() bool {
return n.frequency.Load() > defaultFrequency
return atomic.LoadInt32(&n.frequency) > defaultFrequency
}

func minInt32(a, b int32) int32 {
Expand Down

0 comments on commit 75407e8

Please sign in to comment.