github.com/scottcagno/storage@v1.8.0/pkg/hashmap/openaddr/sharded.go (about) 1 package openaddr 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "github.com/scottcagno/storage/pkg/bits" 7 mathbits "math/bits" 8 "runtime" 9 "sync" 10 ) 11 12 type shard struct { 13 mu sync.RWMutex 14 hm *HashMap // rhh 15 } 16 17 type ShardedHashMap struct { 18 mask uint64 19 hash hashFunc 20 shards []*shard 21 } 22 23 // NewShardedHashMap returns a new hashMap instantiated with the specified size or 24 // the defaultMapSize, whichever is larger 25 func NewShardedHashMap(size uint) *ShardedHashMap { 26 return newShardedHashMap(size, defaultHashFunc) 27 } 28 29 func newShardedHashMap(size uint, fn hashFunc) *ShardedHashMap { 30 shCount := alignShardCount(size) 31 if fn == nil { 32 fn = defaultHashFunc 33 } 34 shm := &ShardedHashMap{ 35 mask: shCount - 1, 36 hash: fn, 37 shards: make([]*shard, shCount), 38 } 39 hmSize := initialMapShardSize(uint16(shCount)) 40 //log.Printf("new sharded hashmap with %d shards, each shard init with %d buckets\n", shCount, hmSize) 41 for i := range shm.shards { 42 shm.shards[i] = &shard{ 43 hm: newHashMap(hmSize, fn), 44 } 45 } 46 return shm 47 } 48 49 func alignShardCount(size uint) uint64 { 50 count := uint(16) 51 for count < size { 52 count *= 2 53 } 54 return uint64(count) 55 } 56 57 func initialMapShardSize(x uint16) uint { 58 return uint(mathbits.Reverse16(x)) / 2 59 } 60 61 func (s *ShardedHashMap) getShard(key string) (uint64, uint64) { 62 // calculate the hashkey value 63 hashkey := s.hash(key) 64 // mask the hashkey to get the initial index 65 i := hashkey & s.mask 66 return i, hashkey 67 } 68 69 func (s *ShardedHashMap) Add(key string, val []byte) ([]byte, bool) { 70 if _, ok := s.lookup(key); ok { 71 return nil, false // returns false if it didnt add 72 } 73 return s.insert(key, val) 74 } 75 76 func (s *ShardedHashMap) Set(key string, val []byte) ([]byte, bool) { 77 return s.insert(key, val) 78 } 79 80 func (s *ShardedHashMap) SetBit(key string, idx uint, bit uint) bool { 81 if bit != 0 && bit != 1 { 82 return false 83 } 84 buk, hashkey := s.getShard(key) 85 s.shards[buk].mu.Lock() 86 ret, _ := s.shards[buk].hm.lookup(hashkey, key) 87 if bit == 1 { 88 bits.RawBytesSetBit(&ret, idx) 89 } 90 if bit == 0 { 91 bits.RawBytesUnsetBit(&ret, idx) 92 } 93 _, _ = s.shards[buk].hm.insert(hashkey, key, ret) 94 s.shards[buk].mu.Unlock() 95 return true 96 } 97 98 func (s *ShardedHashMap) GetBit(key string, idx uint) (uint, bool) { 99 buk, hashkey := s.getShard(key) 100 s.shards[buk].mu.Lock() 101 ret, ok := s.shards[buk].hm.lookup(hashkey, key) 102 if ret == nil || !ok || idx > uint(len(ret)*8) { 103 return 0, false 104 } 105 s.shards[buk].mu.Unlock() 106 bit := bits.RawBytesGetBit(&ret, idx) 107 return bit, bit != 0 108 } 109 110 func (s *ShardedHashMap) SetUint(key string, num uint64) (uint64, bool) { 111 buk, hashkey := s.getShard(key) 112 s.shards[buk].mu.Lock() 113 val := make([]byte, 8) 114 binary.LittleEndian.PutUint64(val, num) 115 ret, ok := s.shards[buk].hm.insert(hashkey, key, val) 116 if !ok { 117 s.shards[buk].mu.Unlock() 118 return 0, false 119 } 120 s.shards[buk].mu.Unlock() 121 return binary.LittleEndian.Uint64(ret), true 122 } 123 124 func (s *ShardedHashMap) GetUint(key string) (uint64, bool) { 125 buk, hashkey := s.getShard(key) 126 s.shards[buk].mu.Lock() 127 ret, ok := s.shards[buk].hm.lookup(hashkey, key) 128 if !ok { 129 s.shards[buk].mu.Unlock() 130 return 0, false 131 } 132 s.shards[buk].mu.Unlock() 133 return binary.LittleEndian.Uint64(ret), true 134 } 135 136 func (s *ShardedHashMap) insert(key string, val []byte) ([]byte, bool) { 137 buk, hashkey := s.getShard(key) 138 s.shards[buk].mu.Lock() 139 pv, ok := s.shards[buk].hm.insert(hashkey, key, val) 140 s.shards[buk].mu.Unlock() 141 return pv, ok 142 } 143 144 func (s *ShardedHashMap) Get(key string) ([]byte, bool) { 145 return s.lookup(key) 146 } 147 148 func (s *ShardedHashMap) lookup(key string) ([]byte, bool) { 149 buk, hashkey := s.getShard(key) 150 s.shards[buk].mu.RLock() 151 pv, ok := s.shards[buk].hm.lookup(hashkey, key) 152 s.shards[buk].mu.RUnlock() 153 return pv, ok 154 } 155 156 func (s *ShardedHashMap) Del(key string) ([]byte, bool) { 157 return s.delete(key) 158 } 159 160 func (s *ShardedHashMap) delete(key string) ([]byte, bool) { 161 buk, hashkey := s.getShard(key) 162 s.shards[buk].mu.Lock() 163 pv, ok := s.shards[buk].hm.delete(hashkey, key) 164 s.shards[buk].mu.Unlock() 165 return pv, ok 166 } 167 168 func (s *ShardedHashMap) Len() int { 169 var length int 170 for i := range s.shards { 171 s.shards[i].mu.Lock() 172 length += s.shards[i].hm.Len() 173 s.shards[i].mu.Unlock() 174 } 175 return length 176 } 177 178 func (s *ShardedHashMap) Range(it Iterator) { 179 for i := range s.shards { 180 s.shards[i].mu.Lock() 181 s.shards[i].hm.Range(it) 182 s.shards[i].mu.Unlock() 183 } 184 } 185 186 func (s *ShardedHashMap) Stats() { 187 for i := range s.shards { 188 s.shards[i].mu.Lock() 189 if pf := s.shards[i].hm.PercentFull(); pf > 0 { 190 fmt.Printf("shard %d, fill percent: %.4f\n", i, pf) 191 } 192 s.shards[i].mu.Unlock() 193 } 194 } 195 196 func (s *ShardedHashMap) Close() { 197 for i := range s.shards { 198 s.shards[i].mu.Lock() 199 destroyMap(s.shards[i].hm) 200 s.shards[i].mu.Unlock() 201 } 202 s.shards = nil 203 runtime.GC() 204 }