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  }