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  }