tinylfu.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. package cache
  2. const (
  3. samplesMultiplier = 8
  4. insertionsMultiplier = 2
  5. countersMultiplier = 1
  6. falsePositiveProbability = 0.1
  7. admissionRatio = 0.01
  8. )
  9. // tinyLFU is an implementation of TinyLFU. It utilizing 4bit Count Min Sketch
  10. // and Bloom Filter as a Doorkeeper and Segmented LRU for long term retention.
  11. // See https://arxiv.org/pdf/1512.00727v2.pdf
  12. type tinyLFU struct {
  13. filter bloomFilter // 1bit counter
  14. counter countMinSketch // 4bit counter
  15. additions int
  16. samples int
  17. lru lruCache
  18. slru slruCache
  19. }
  20. func (l *tinyLFU) init(c *cache, cap int) {
  21. if cap > 0 {
  22. // Only enable doorkeeper when capacity is finite.
  23. l.samples = samplesMultiplier * cap
  24. l.filter.init(insertionsMultiplier*cap, falsePositiveProbability)
  25. l.counter.init(countersMultiplier * cap)
  26. }
  27. lruCap := int(float64(cap) * admissionRatio)
  28. l.lru.init(c, lruCap)
  29. l.slru.init(c, cap-lruCap)
  30. }
  31. func (l *tinyLFU) write(en *entry) *entry {
  32. if l.lru.cap <= 0 {
  33. return l.slru.write(en)
  34. }
  35. l.increase(en.hash)
  36. candidate := l.lru.write(en)
  37. if candidate == nil {
  38. return nil
  39. }
  40. victim := l.slru.victim()
  41. if victim == nil {
  42. return l.slru.write(candidate)
  43. }
  44. // Determine one going to be evicted
  45. candidateFreq := l.estimate(candidate.hash)
  46. victimFreq := l.estimate(victim.hash)
  47. if candidateFreq > victimFreq {
  48. return l.slru.write(candidate)
  49. }
  50. return candidate
  51. }
  52. func (l *tinyLFU) access(en *entry) {
  53. l.increase(en.hash)
  54. if en.listID == admissionWindow {
  55. l.lru.access(en)
  56. } else {
  57. l.slru.access(en)
  58. }
  59. }
  60. func (l *tinyLFU) remove(en *entry) *entry {
  61. if en.listID == admissionWindow {
  62. return l.lru.remove(en)
  63. }
  64. return l.slru.remove(en)
  65. }
  66. // increase adds the given hash to the filter and counter.
  67. func (l *tinyLFU) increase(h uint64) {
  68. if l.samples <= 0 {
  69. return
  70. }
  71. l.additions++
  72. if l.additions >= l.samples {
  73. l.filter.reset()
  74. l.counter.reset()
  75. l.additions = 0
  76. }
  77. if l.filter.put(h) {
  78. l.counter.add(h)
  79. }
  80. }
  81. // estimate estimates frequency of the given hash value.
  82. func (l *tinyLFU) estimate(h uint64) uint8 {
  83. freq := l.counter.estimate(h)
  84. if l.filter.contains(h) {
  85. freq++
  86. }
  87. return freq
  88. }
  89. // iterate walks through all lists by access time.
  90. func (l *tinyLFU) iterate(fn func(en *entry) bool) {
  91. l.slru.iterate(fn)
  92. l.lru.iterate(fn)
  93. }