cache.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package freecache
  2. import (
  3. "sync"
  4. "sync/atomic"
  5. "github.com/spaolacci/murmur3"
  6. )
  7. type Cache struct {
  8. locks [256]sync.Mutex
  9. segments [256]segment
  10. hitCount int64
  11. missCount int64
  12. }
  13. func hashFunc(data []byte) uint64 {
  14. return murmur3.Sum64(data)
  15. }
  16. // The cache size will be set to 512KB at minimum.
  17. // If the size is set relatively large, you should call
  18. // `debug.SetGCPercent()`, set it to a much smaller value
  19. // to limit the memory consumption and GC pause time.
  20. func NewCache(size int) (cache *Cache) {
  21. if size < 512*1024 {
  22. size = 512 * 1024
  23. }
  24. cache = new(Cache)
  25. for i := 0; i < 256; i++ {
  26. cache.segments[i] = newSegment(size/256, i)
  27. }
  28. return
  29. }
  30. // If the key is larger than 65535 or value is larger than 1/1024 of the cache size,
  31. // the entry will not be written to the cache. expireSeconds <= 0 means no expire,
  32. // but it can be evicted when cache is full.
  33. func (cache *Cache) Set(key, value []byte, expireSeconds int) (err error) {
  34. hashVal := hashFunc(key)
  35. segId := hashVal & 255
  36. cache.locks[segId].Lock()
  37. err = cache.segments[segId].set(key, value, hashVal, expireSeconds)
  38. cache.locks[segId].Unlock()
  39. return
  40. }
  41. // Get the value or not found error.
  42. func (cache *Cache) Get(key []byte) (value []byte, err error) {
  43. hashVal := hashFunc(key)
  44. segId := hashVal & 255
  45. cache.locks[segId].Lock()
  46. value, err = cache.segments[segId].get(key, hashVal)
  47. cache.locks[segId].Unlock()
  48. if err == nil {
  49. atomic.AddInt64(&cache.hitCount, 1)
  50. } else {
  51. atomic.AddInt64(&cache.missCount, 1)
  52. }
  53. return
  54. }
  55. func (cache *Cache) Del(key []byte) (affected bool) {
  56. hashVal := hashFunc(key)
  57. segId := hashVal & 255
  58. cache.locks[segId].Lock()
  59. affected = cache.segments[segId].del(key, hashVal)
  60. cache.locks[segId].Unlock()
  61. return
  62. }
  63. func (cache *Cache) EvacuateCount() (count int64) {
  64. for i := 0; i < 256; i++ {
  65. count += atomic.LoadInt64(&cache.segments[i].totalEvacuate)
  66. }
  67. return
  68. }
  69. func (cache *Cache) EntryCount() (entryCount int64) {
  70. for i := 0; i < 256; i++ {
  71. entryCount += atomic.LoadInt64(&cache.segments[i].entryCount)
  72. }
  73. return
  74. }
  75. // The average unix timestamp when a entry being accessed.
  76. // Entries have greater access time will be evacuated when it
  77. // is about to be overwritten by new value.
  78. func (cache *Cache) AverageAccessTime() int64 {
  79. var entryCount, totalTime int64
  80. for i := 0; i < 256; i++ {
  81. totalTime += atomic.LoadInt64(&cache.segments[i].totalTime)
  82. entryCount += atomic.LoadInt64(&cache.segments[i].totalCount)
  83. }
  84. if entryCount == 0 {
  85. return 0
  86. } else {
  87. return totalTime / entryCount
  88. }
  89. }
  90. func (cache *Cache) HitCount() int64 {
  91. return atomic.LoadInt64(&cache.hitCount)
  92. }
  93. func (cache *Cache) LookupCount() int64 {
  94. return atomic.LoadInt64(&cache.hitCount) + atomic.LoadInt64(&cache.missCount)
  95. }
  96. func (cache *Cache) HitRate() float64 {
  97. lookupCount := cache.LookupCount()
  98. if lookupCount == 0 {
  99. return 0
  100. } else {
  101. return float64(cache.HitCount()) / float64(lookupCount)
  102. }
  103. }
  104. func (cache *Cache) OverwriteCount() (overwriteCount int64) {
  105. for i := 0; i < 256; i++ {
  106. overwriteCount += atomic.LoadInt64(&cache.segments[i].overwrites)
  107. }
  108. return
  109. }
  110. func (cache *Cache) Clear() {
  111. for i := 0; i < 256; i++ {
  112. cache.locks[i].Lock()
  113. newSeg := newSegment(len(cache.segments[i].rb.data), i)
  114. cache.segments[i] = newSeg
  115. cache.locks[i].Unlock()
  116. }
  117. atomic.StoreInt64(&cache.hitCount, 0)
  118. atomic.StoreInt64(&cache.missCount, 0)
  119. }