lru.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package cache
  2. import (
  3. "container/list"
  4. )
  5. // lruCache is a LRU cache.
  6. type lruCache struct {
  7. cache *cache
  8. cap int
  9. ls list.List
  10. }
  11. // init initializes cache list.
  12. func (l *lruCache) init(c *cache, cap int) {
  13. l.cache = c
  14. l.cap = cap
  15. l.ls.Init()
  16. }
  17. // write adds new entry to the cache and returns evicted entry if necessary.
  18. func (l *lruCache) write(en *entry) *entry {
  19. // Fast path
  20. if en.accessList != nil {
  21. // Entry existed, update its status instead.
  22. l.markAccess(en)
  23. return nil
  24. }
  25. // Try to add new entry to the list
  26. cen := l.cache.getOrSet(en)
  27. if cen == nil {
  28. // Brand new entry, add to the LRU list.
  29. en.accessList = l.ls.PushFront(en)
  30. } else {
  31. // Entry has already been added, update its value instead.
  32. cen.setValue(en.getValue())
  33. cen.setWriteTime(en.getWriteTime())
  34. if cen.accessList == nil {
  35. // Entry is loaded to the cache but not yet registered.
  36. cen.accessList = l.ls.PushFront(cen)
  37. } else {
  38. l.markAccess(cen)
  39. }
  40. }
  41. if l.cap > 0 && l.ls.Len() > l.cap {
  42. // Remove the last element when capacity exceeded.
  43. en = getEntry(l.ls.Back())
  44. return l.remove(en)
  45. }
  46. return nil
  47. }
  48. // access updates cache entry for a get.
  49. func (l *lruCache) access(en *entry) {
  50. if en.accessList != nil {
  51. l.markAccess(en)
  52. }
  53. }
  54. // markAccess marks the element has just been accessed.
  55. // en.accessList must not be null.
  56. func (l *lruCache) markAccess(en *entry) {
  57. l.ls.MoveToFront(en.accessList)
  58. }
  59. // remove removes an entry from the cache.
  60. func (l *lruCache) remove(en *entry) *entry {
  61. if en.accessList == nil {
  62. // Already deleted
  63. return nil
  64. }
  65. l.cache.delete(en)
  66. l.ls.Remove(en.accessList)
  67. en.accessList = nil
  68. return en
  69. }
  70. // iterate walks through all lists by access time.
  71. func (l *lruCache) iterate(fn func(en *entry) bool) {
  72. iterateListFromBack(&l.ls, fn)
  73. }
  74. const (
  75. admissionWindow uint8 = iota
  76. probationSegment
  77. protectedSegment
  78. )
  79. const (
  80. protectedRatio = 0.8
  81. )
  82. // slruCache is a segmented LRU.
  83. // See http://highscalability.com/blog/2016/1/25/design-of-a-modern-cache.html
  84. type slruCache struct {
  85. cache *cache
  86. probationCap int
  87. probationLs list.List
  88. protectedCap int
  89. protectedLs list.List
  90. }
  91. // init initializes the cache list.
  92. func (l *slruCache) init(c *cache, cap int) {
  93. l.cache = c
  94. l.protectedCap = int(float64(cap) * protectedRatio)
  95. l.probationCap = cap - l.protectedCap
  96. l.probationLs.Init()
  97. l.protectedLs.Init()
  98. }
  99. // length returns total number of entries in the cache.
  100. func (l *slruCache) length() int {
  101. return l.probationLs.Len() + l.protectedLs.Len()
  102. }
  103. // write adds new entry to the cache and returns evicted entry if necessary.
  104. func (l *slruCache) write(en *entry) *entry {
  105. // Fast path
  106. if en.accessList != nil {
  107. // Entry existed, update its value instead.
  108. l.markAccess(en)
  109. return nil
  110. }
  111. // Try to add new entry to the probation segment.
  112. cen := l.cache.getOrSet(en)
  113. if cen == nil {
  114. // Brand new entry, add to the probation segment.
  115. en.listID = probationSegment
  116. en.accessList = l.probationLs.PushFront(en)
  117. } else {
  118. // Entry has already been added, update its value instead.
  119. cen.setValue(en.getValue())
  120. cen.setWriteTime(en.getWriteTime())
  121. if cen.accessList == nil {
  122. // Entry is loaded to the cache but not yet registered.
  123. cen.listID = probationSegment
  124. cen.accessList = l.probationLs.PushFront(cen)
  125. } else {
  126. l.markAccess(cen)
  127. }
  128. }
  129. // The probation list can exceed its capacity if number of entries
  130. // is still under total allowed capacity.
  131. if l.probationCap > 0 && l.probationLs.Len() > l.probationCap &&
  132. l.length() > (l.probationCap+l.protectedCap) {
  133. // Remove the last element when capacity exceeded.
  134. en = getEntry(l.probationLs.Back())
  135. return l.remove(en)
  136. }
  137. return nil
  138. }
  139. // access updates cache entry for a get.
  140. func (l *slruCache) access(en *entry) {
  141. if en.accessList != nil {
  142. l.markAccess(en)
  143. }
  144. }
  145. // markAccess marks the element has just been accessed.
  146. // en.accessList must not be null.
  147. func (l *slruCache) markAccess(en *entry) {
  148. if en.listID == protectedSegment {
  149. // Already in the protected segment.
  150. l.protectedLs.MoveToFront(en.accessList)
  151. return
  152. }
  153. // The entry is currently in the probation segment, promote it to the protected segment.
  154. en.listID = protectedSegment
  155. l.probationLs.Remove(en.accessList)
  156. en.accessList = l.protectedLs.PushFront(en)
  157. if l.protectedCap > 0 && l.protectedLs.Len() > l.protectedCap {
  158. // Protected list capacity exceeded, move the last entry in the protected segment to
  159. // the probation segment.
  160. en = getEntry(l.protectedLs.Back())
  161. en.listID = probationSegment
  162. l.protectedLs.Remove(en.accessList)
  163. en.accessList = l.probationLs.PushFront(en)
  164. }
  165. }
  166. // remove removes an entry from the cache and returns the removed entry or nil
  167. // if it is not found.
  168. func (l *slruCache) remove(en *entry) *entry {
  169. if en.accessList == nil {
  170. return nil
  171. }
  172. l.cache.delete(en)
  173. if en.listID == protectedSegment {
  174. l.protectedLs.Remove(en.accessList)
  175. } else {
  176. l.probationLs.Remove(en.accessList)
  177. }
  178. en.accessList = nil
  179. return en
  180. }
  181. // victim returns the last entry in probation list if total entries reached the limit.
  182. func (l *slruCache) victim() *entry {
  183. if l.probationCap <= 0 || l.length() < (l.probationCap+l.protectedCap) {
  184. return nil
  185. }
  186. el := l.probationLs.Back()
  187. if el == nil {
  188. return nil
  189. }
  190. return getEntry(el)
  191. }
  192. // iterate walks through all lists by access time.
  193. func (l *slruCache) iterate(fn func(en *entry) bool) {
  194. iterateListFromBack(&l.protectedLs, fn)
  195. iterateListFromBack(&l.probationLs, fn)
  196. }