go.uber.org/yarpc@v1.72.1/peer/hashring32/internal/radixsort32/radixsort.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package radixsort32 22 23 import ( 24 "math/bits" 25 "sort" 26 "sync" 27 ) 28 29 const ( 30 defaultRadix = 8 31 defaultMinLen = 1000 32 defaultMaxLen = 200000 33 ) 34 35 // RadixSorter32 is a radix sorter for sorting []uint32 36 type RadixSorter32 struct { 37 // radix can be 1, 2, 4, 8, 16 38 // cannot be 32 because the size of counting buffer overflows integer 39 radix uint 40 // for slice shorter than minLength it uses regular sort algorithm 41 minLength int 42 // for slice longer than maxLength it allocate buffer dynamically 43 // which increases garbage collector's pressure = 44 maxLength int 45 46 // immutable after construction 47 mask uint32 48 keyOffsets []uint 49 50 // buffer used to reduce memory allocation 51 buffer buf 52 } 53 54 // Option is an option for the radix sorter constructor. 55 type Option interface { 56 apply(*RadixSorter32) 57 } 58 59 type noOption struct{} 60 61 func (noOption) apply(*RadixSorter32) {} 62 63 // Radix specifies the radix sorter's radix. 64 // 65 // Radix may be 1, 2, 4, 8, or 16. 66 func Radix(radix int) Option { 67 for i := range validRadixes { 68 if validRadixes[i] == radix { 69 return radixOption{radix: radix} 70 } 71 } 72 // fallback to default value 73 return noOption{} 74 } 75 76 var validRadixes = []int{1, 2, 4, 8, 16} 77 78 type radixOption struct { 79 radix int 80 } 81 82 func (o radixOption) apply(r *RadixSorter32) { 83 r.radix = uint(o.radix) 84 } 85 86 // MinLen specifies minimum length of slice to use radix sort. 87 // 88 // The radix sorter will use quick sort for shorter slices. 89 func MinLen(minLen int) Option { 90 return minLenOption{minLen: minLen} 91 } 92 93 type minLenOption struct { 94 minLen int 95 } 96 97 func (o minLenOption) apply(r *RadixSorter32) { 98 if o.minLen < 0 { 99 o.minLen = 0 100 } 101 r.minLength = o.minLen 102 if o.minLen > r.maxLength { 103 r.maxLength = r.minLength 104 } 105 } 106 107 // MaxLen is maximum length of slice that can utilize buffers in pool, 108 // would use dynamic buffer allocation for larger slices 109 func MaxLen(maxLen int) Option { 110 return maxLenOption{maxLen: maxLen} 111 } 112 113 type maxLenOption struct { 114 maxLen int 115 } 116 117 func (o maxLenOption) apply(r *RadixSorter32) { 118 if o.maxLen >= r.minLength { 119 r.maxLength = o.maxLen 120 } 121 r.maxLength = o.maxLen 122 if o.maxLen < r.minLength { 123 r.minLength = r.maxLength 124 } 125 } 126 127 // New creates a radix sorter for sorting []uint32 128 func New(options ...Option) *RadixSorter32 { 129 rs := &RadixSorter32{ 130 radix: defaultRadix, 131 minLength: defaultMinLen, 132 maxLength: defaultMaxLen, 133 } 134 for _, opt := range options { 135 opt.apply(rs) 136 } 137 // set key mask, e.g., 0xFF when radix is 8 138 for i := 0; i < int(rs.radix); i++ { 139 rs.mask = bits.RotateLeft32(rs.mask, 1) | 1 140 } 141 142 rs.buffer = newBuffer(rs.maxLength, int(rs.mask)+1) 143 144 iterations := 32 / rs.radix 145 rs.keyOffsets = make([]uint, iterations) 146 for i := range rs.keyOffsets { 147 rs.keyOffsets[i] = uint(i) * rs.radix 148 } 149 return rs 150 } 151 152 // Sort sorts a slice of type []uint32 153 func (r *RadixSorter32) Sort(origin []uint32) { 154 if len(origin) <= r.minLength { 155 sort.Slice(origin, func(i, j int) bool { 156 return origin[i] < origin[j] 157 }) 158 return 159 } 160 161 // Utilize buffer from pool or allocate slice when size too large 162 length := len(origin) 163 var buf, swap *[]uint32 164 if len(origin) > r.maxLength { 165 tmp := make([]uint32, length) 166 swap = &tmp 167 } else { 168 buf = r.buffer.getSwap() 169 t := (*buf)[:len(origin)] 170 swap = &t 171 defer r.buffer.putSwap(buf) 172 } 173 174 var key uint32 175 176 offset := r.buffer.getCounters() 177 defer r.buffer.putCounters(offset) 178 179 counts := r.buffer.getCounters() 180 defer r.buffer.putCounters(counts) 181 182 for _, keyOffset := range r.keyOffsets { 183 keyMask := uint32(r.mask << keyOffset) 184 185 // counting 186 for i := range *counts { 187 (*counts)[i] = 0 188 } 189 190 for _, h := range origin { 191 key = r.mask & ((h & keyMask) >> keyOffset) 192 (*counts)[key]++ 193 } 194 for i := range *offset { 195 if i == 0 { 196 (*offset)[0] = 0 197 continue 198 } 199 (*offset)[i] = (*offset)[i-1] + (*counts)[i-1] 200 } 201 202 for _, h := range origin { 203 key = r.mask & ((h & keyMask) >> keyOffset) 204 (*swap)[(*offset)[key]] = h 205 (*offset)[key]++ 206 } 207 *swap, origin = origin, *swap 208 } 209 } 210 211 // buffer 212 type buf struct { 213 swapPool sync.Pool 214 countersPool sync.Pool 215 } 216 217 func (b *buf) getSwap() *[]uint32 { 218 buf := *b.swapPool.Get().(*[]uint32) 219 bb := buf[:] 220 return &bb 221 } 222 223 func (b *buf) putSwap(buf *[]uint32) { 224 b.swapPool.Put(buf) 225 } 226 227 func (b *buf) getCounters() *[]int { 228 buf := *b.countersPool.Get().(*[]int) 229 bb := buf[:] 230 return &bb 231 } 232 233 func (b *buf) putCounters(buf *[]int) { 234 b.countersPool.Put(buf) 235 } 236 237 func newBuffer(swapSize, countersSize int) buf { 238 return buf{ 239 swapPool: sync.Pool{ 240 New: func() interface{} { 241 b := make([]uint32, swapSize) 242 return &b 243 }, 244 }, 245 countersPool: sync.Pool{ 246 New: func() interface{} { 247 b := make([]int, countersSize) 248 return &b 249 }, 250 }, 251 } 252 }