forked from cerc-io/plugeth
289b30715d
This commit converts the dependency management from Godeps to the vendor folder, also switching the tool from godep to trash. Since the upstream tool lacks a few features proposed via a few PRs, until those PRs are merged in (if), use github.com/karalabe/trash. You can update dependencies via trash --update. All dependencies have been updated to their latest version. Parts of the build system are reworked to drop old notions of Godeps and invocation of the go vet command so that it doesn't run against the vendor folder, as that will just blow up during vetting. The conversion drops OpenCL (and hence GPU mining support) from ethash and our codebase. The short reasoning is that there's noone to maintain and having opencl libs in our deps messes up builds as go install ./... tries to build them, failing with unsatisfied link errors for the C OpenCL deps. golang.org/x/net/context is not vendored in. We expect it to be fetched by the user (i.e. using go get). To keep ci.go builds reproducible the package is "vendored" in build/_vendor.
213 lines
4.9 KiB
Go
213 lines
4.9 KiB
Go
package lru
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/golang-lru/simplelru"
|
|
)
|
|
|
|
const (
|
|
// Default2QRecentRatio is the ratio of the 2Q cache dedicated
|
|
// to recently added entries that have only been accessed once.
|
|
Default2QRecentRatio = 0.25
|
|
|
|
// Default2QGhostEntries is the default ratio of ghost
|
|
// entries kept to track entries recently evicted
|
|
Default2QGhostEntries = 0.50
|
|
)
|
|
|
|
// TwoQueueCache is a thread-safe fixed size 2Q cache.
|
|
// 2Q is an enhancement over the standard LRU cache
|
|
// in that it tracks both frequently and recently used
|
|
// entries separately. This avoids a burst in access to new
|
|
// entries from evicting frequently used entries. It adds some
|
|
// additional tracking overhead to the standard LRU cache, and is
|
|
// computationally about 2x the cost, and adds some metadata over
|
|
// head. The ARCCache is similar, but does not require setting any
|
|
// parameters.
|
|
type TwoQueueCache struct {
|
|
size int
|
|
recentSize int
|
|
|
|
recent *simplelru.LRU
|
|
frequent *simplelru.LRU
|
|
recentEvict *simplelru.LRU
|
|
lock sync.RWMutex
|
|
}
|
|
|
|
// New2Q creates a new TwoQueueCache using the default
|
|
// values for the parameters.
|
|
func New2Q(size int) (*TwoQueueCache, error) {
|
|
return New2QParams(size, Default2QRecentRatio, Default2QGhostEntries)
|
|
}
|
|
|
|
// New2QParams creates a new TwoQueueCache using the provided
|
|
// parameter values.
|
|
func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCache, error) {
|
|
if size <= 0 {
|
|
return nil, fmt.Errorf("invalid size")
|
|
}
|
|
if recentRatio < 0.0 || recentRatio > 1.0 {
|
|
return nil, fmt.Errorf("invalid recent ratio")
|
|
}
|
|
if ghostRatio < 0.0 || ghostRatio > 1.0 {
|
|
return nil, fmt.Errorf("invalid ghost ratio")
|
|
}
|
|
|
|
// Determine the sub-sizes
|
|
recentSize := int(float64(size) * recentRatio)
|
|
evictSize := int(float64(size) * ghostRatio)
|
|
|
|
// Allocate the LRUs
|
|
recent, err := simplelru.NewLRU(size, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
frequent, err := simplelru.NewLRU(size, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
recentEvict, err := simplelru.NewLRU(evictSize, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Initialize the cache
|
|
c := &TwoQueueCache{
|
|
size: size,
|
|
recentSize: recentSize,
|
|
recent: recent,
|
|
frequent: frequent,
|
|
recentEvict: recentEvict,
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) {
|
|
c.lock.Lock()
|
|
defer c.lock.Unlock()
|
|
|
|
// Check if this is a frequent value
|
|
if val, ok := c.frequent.Get(key); ok {
|
|
return val, ok
|
|
}
|
|
|
|
// If the value is contained in recent, then we
|
|
// promote it to frequent
|
|
if val, ok := c.recent.Peek(key); ok {
|
|
c.recent.Remove(key)
|
|
c.frequent.Add(key, val)
|
|
return val, ok
|
|
}
|
|
|
|
// No hit
|
|
return nil, false
|
|
}
|
|
|
|
func (c *TwoQueueCache) Add(key, value interface{}) {
|
|
c.lock.Lock()
|
|
defer c.lock.Unlock()
|
|
|
|
// Check if the value is frequently used already,
|
|
// and just update the value
|
|
if c.frequent.Contains(key) {
|
|
c.frequent.Add(key, value)
|
|
return
|
|
}
|
|
|
|
// Check if the value is recently used, and promote
|
|
// the value into the frequent list
|
|
if c.recent.Contains(key) {
|
|
c.recent.Remove(key)
|
|
c.frequent.Add(key, value)
|
|
return
|
|
}
|
|
|
|
// If the value was recently evicted, add it to the
|
|
// frequently used list
|
|
if c.recentEvict.Contains(key) {
|
|
c.ensureSpace(true)
|
|
c.recentEvict.Remove(key)
|
|
c.frequent.Add(key, value)
|
|
return
|
|
}
|
|
|
|
// Add to the recently seen list
|
|
c.ensureSpace(false)
|
|
c.recent.Add(key, value)
|
|
return
|
|
}
|
|
|
|
// ensureSpace is used to ensure we have space in the cache
|
|
func (c *TwoQueueCache) ensureSpace(recentEvict bool) {
|
|
// If we have space, nothing to do
|
|
recentLen := c.recent.Len()
|
|
freqLen := c.frequent.Len()
|
|
if recentLen+freqLen < c.size {
|
|
return
|
|
}
|
|
|
|
// If the recent buffer is larger than
|
|
// the target, evict from there
|
|
if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) {
|
|
k, _, _ := c.recent.RemoveOldest()
|
|
c.recentEvict.Add(k, nil)
|
|
return
|
|
}
|
|
|
|
// Remove from the frequent list otherwise
|
|
c.frequent.RemoveOldest()
|
|
}
|
|
|
|
func (c *TwoQueueCache) Len() int {
|
|
c.lock.RLock()
|
|
defer c.lock.RUnlock()
|
|
return c.recent.Len() + c.frequent.Len()
|
|
}
|
|
|
|
func (c *TwoQueueCache) Keys() []interface{} {
|
|
c.lock.RLock()
|
|
defer c.lock.RUnlock()
|
|
k1 := c.frequent.Keys()
|
|
k2 := c.recent.Keys()
|
|
return append(k1, k2...)
|
|
}
|
|
|
|
func (c *TwoQueueCache) Remove(key interface{}) {
|
|
c.lock.Lock()
|
|
defer c.lock.Unlock()
|
|
if c.frequent.Remove(key) {
|
|
return
|
|
}
|
|
if c.recent.Remove(key) {
|
|
return
|
|
}
|
|
if c.recentEvict.Remove(key) {
|
|
return
|
|
}
|
|
}
|
|
|
|
func (c *TwoQueueCache) Purge() {
|
|
c.lock.Lock()
|
|
defer c.lock.Unlock()
|
|
c.recent.Purge()
|
|
c.frequent.Purge()
|
|
c.recentEvict.Purge()
|
|
}
|
|
|
|
func (c *TwoQueueCache) Contains(key interface{}) bool {
|
|
c.lock.RLock()
|
|
defer c.lock.RUnlock()
|
|
return c.frequent.Contains(key) || c.recent.Contains(key)
|
|
}
|
|
|
|
func (c *TwoQueueCache) Peek(key interface{}) (interface{}, bool) {
|
|
c.lock.RLock()
|
|
defer c.lock.RUnlock()
|
|
if val, ok := c.frequent.Peek(key); ok {
|
|
return val, ok
|
|
}
|
|
return c.recent.Peek(key)
|
|
}
|