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  }