MultiGet returns values and errors for the given keys. The returned slices have the same length as keys; values[i] and errs[i] correspond to keys[i]. A miss is represented by values[i] == nil and errs[i] == ErrNotFound. MultiGet reduces lock contention by grouping keys by segment and acquiring each
(keys [][]byte)
| 95 | // the duration of all keys in that segment), which can increase Get tail |
| 96 | // latency when MultiGet and Get run concurrently. |
| 97 | func (cache *Cache) MultiGet(keys [][]byte) (values [][]byte, errs []error) { |
| 98 | n := len(keys) |
| 99 | if n == 0 { |
| 100 | return nil, nil |
| 101 | } |
| 102 | values = make([][]byte, n) |
| 103 | errs = make([]error, n) |
| 104 | type keyLoc struct { |
| 105 | idx int |
| 106 | hashVal uint64 |
| 107 | } |
| 108 | var groups [segmentCount][]keyLoc |
| 109 | for i, key := range keys { |
| 110 | hashVal := hashFunc(key) |
| 111 | segID := hashVal & segmentAndOpVal |
| 112 | groups[segID] = append(groups[segID], keyLoc{idx: i, hashVal: hashVal}) |
| 113 | } |
| 114 | for segID := 0; segID < segmentCount; segID++ { |
| 115 | batch := groups[segID] |
| 116 | if len(batch) == 0 { |
| 117 | continue |
| 118 | } |
| 119 | cache.locks[segID].Lock() |
| 120 | for _, loc := range batch { |
| 121 | value, _, err := cache.segments[segID].get(keys[loc.idx], nil, loc.hashVal, false) |
| 122 | values[loc.idx] = value |
| 123 | errs[loc.idx] = err |
| 124 | } |
| 125 | cache.locks[segID].Unlock() |
| 126 | } |
| 127 | return values, errs |
| 128 | } |
| 129 | |
| 130 | // GetFn is equivalent to Get or GetWithBuf, but it attempts to be zero-copy, |
| 131 | // calling the provided function with slice view over the current underlying |