github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/perf/lru/walbuf.go (about) 1 //nolint:revive 2 package lru 3 4 import "sync" 5 6 const ( 7 // walBufSize must be power of two 8 walBufSize = 1024 9 walSetSize = walBufSize * 2 10 walSetMask = walSetSize - 1 11 ) 12 13 var walbufpool sync.Pool 14 15 func newWalBuf() *walbuf { 16 if buf := walbufpool.Get(); buf != nil { 17 return buf.(*walbuf) 18 } 19 return &walbuf{} 20 } 21 22 // 关于 promotion 和 walbuf 的并发安全性 23 // 24 // 1. Cache.buf 永远不为 nil, 当 Cache.buf 写满时,promote 方法中创建新的 walbuf 25 // 并使用 CAS 操作赋值给 Cache.buf, 成功执行 CAS 的 goroutine 负责触发 flushBuf; 26 // 2. Cache.promote 方法中对 buf.p 原子加一,每个 goroutine 写入自己拿到的索引位置, 27 // 不同 goroutine 不会同时写入同一个内存位置; 28 // 3. 当 Cache.promote 方法被调用时,调用者(Get相关方法)一定持有了 RLock, 29 // 在 flush walbuf 时,会持有排他锁,因此 promote 方法和 flushBuf 方法一定不会 30 // 同时执行,flushBuf 函数可以排他地读写 walbuf 的数据; 31 // 4. flushBuf 方法接受的 walbuf 参数是从 Cache.buf 中 CAS 出来的,又因为 promote 32 // 和 flushBuf 方法的互斥性,因此保证了一个 walbuf 被传递给 flushBuf 方法后, 33 // 不会被其他任何 goroutine 持有,flushBuf 结束后,可以安全放回 walbufpool 重用; 34 35 // walbuf helps to reduce lock-contention of read requests from the cache. 36 type walbuf struct { 37 b [walBufSize]uint32 38 s fastHashset 39 p int32 40 } 41 42 func (wbuf *walbuf) reset() { 43 wbuf.p = 0 44 for i := range wbuf.s { // memclr 45 wbuf.s[i] = 0 46 } 47 } 48 49 func (wbuf *walbuf) deduplicate() []uint32 { 50 // Note that we have already checked wbuf.p > 0. 51 ln := wbuf.p 52 if ln > walBufSize { 53 ln = walBufSize 54 } 55 56 set := &wbuf.s 57 b, p := wbuf.b[:], ln-1 58 for i := ln - 1; i >= 0; i-- { 59 idx := b[i] 60 if !set.has(idx) { 61 set.add(idx) 62 b[p] = idx 63 p-- 64 } 65 } 66 return b[p+1 : ln] 67 } 68 69 type fastHashset [walSetSize]uint32 70 71 // intPhi is for scrambling the values 72 const intPhi = 0x9E3779B9 73 74 //nolint:unused 75 func phiMix(x int64) int64 { 76 h := x * intPhi 77 return h ^ (h >> 16) 78 } 79 80 func (s *fastHashset) add(value uint32) { 81 value += 1 82 83 // Manually inline function phiMix. 84 h := int64(value) * intPhi 85 ptr := h ^ (h >> 16) 86 87 for { 88 ptr &= walSetMask 89 k := s[ptr] 90 if k == 0 { 91 s[ptr] = value 92 return 93 } 94 if k == value { 95 return 96 } 97 ptr += 1 98 } 99 } 100 101 func (s *fastHashset) has(value uint32) bool { 102 value += 1 103 104 // Manually inline function phiMix. 105 h := int64(value) * intPhi 106 ptr := h ^ (h >> 16) 107 108 for { 109 ptr &= walSetMask 110 k := s[ptr] 111 if k == value { 112 return true 113 } 114 if k == 0 { 115 return false 116 } 117 ptr += 1 118 } 119 }