123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- package cache
- const (
- samplesMultiplier = 8
- insertionsMultiplier = 2
- countersMultiplier = 1
- falsePositiveProbability = 0.1
- admissionRatio = 0.01
- )
- // tinyLFU is an implementation of TinyLFU. It utilizing 4bit Count Min Sketch
- // and Bloom Filter as a Doorkeeper and Segmented LRU for long term retention.
- // See https://arxiv.org/pdf/1512.00727v2.pdf
- type tinyLFU struct {
- filter bloomFilter // 1bit counter
- counter countMinSketch // 4bit counter
- additions int
- samples int
- lru lruCache
- slru slruCache
- }
- func (l *tinyLFU) init(c *cache, cap int) {
- if cap > 0 {
- // Only enable doorkeeper when capacity is finite.
- l.samples = samplesMultiplier * cap
- l.filter.init(insertionsMultiplier*cap, falsePositiveProbability)
- l.counter.init(countersMultiplier * cap)
- }
- lruCap := int(float64(cap) * admissionRatio)
- l.lru.init(c, lruCap)
- l.slru.init(c, cap-lruCap)
- }
- func (l *tinyLFU) write(en *entry) *entry {
- if l.lru.cap <= 0 {
- return l.slru.write(en)
- }
- l.increase(en.hash)
- candidate := l.lru.write(en)
- if candidate == nil {
- return nil
- }
- victim := l.slru.victim()
- if victim == nil {
- return l.slru.write(candidate)
- }
- // Determine one going to be evicted
- candidateFreq := l.estimate(candidate.hash)
- victimFreq := l.estimate(victim.hash)
- if candidateFreq > victimFreq {
- return l.slru.write(candidate)
- }
- return candidate
- }
- func (l *tinyLFU) access(en *entry) {
- l.increase(en.hash)
- if en.listID == admissionWindow {
- l.lru.access(en)
- } else {
- l.slru.access(en)
- }
- }
- func (l *tinyLFU) remove(en *entry) *entry {
- if en.listID == admissionWindow {
- return l.lru.remove(en)
- }
- return l.slru.remove(en)
- }
- // increase adds the given hash to the filter and counter.
- func (l *tinyLFU) increase(h uint64) {
- if l.samples <= 0 {
- return
- }
- l.additions++
- if l.additions >= l.samples {
- l.filter.reset()
- l.counter.reset()
- l.additions = 0
- }
- if l.filter.put(h) {
- l.counter.add(h)
- }
- }
- // estimate estimates frequency of the given hash value.
- func (l *tinyLFU) estimate(h uint64) uint8 {
- freq := l.counter.estimate(h)
- if l.filter.contains(h) {
- freq++
- }
- return freq
- }
- // iterate walks through all lists by access time.
- func (l *tinyLFU) iterate(fn func(en *entry) bool) {
- l.slru.iterate(fn)
- l.lru.iterate(fn)
- }
|