github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/quicksort_tmpl.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 // Copyright 2009 The Go Authors. All rights reserved. 12 // Use of this source code is governed by a BSD-style 13 // license that can be found in the LICENSE file. 14 15 // {{/* 16 // +build execgen_template 17 // */}} 18 19 package colexec 20 21 import "context" 22 23 // This file is copied from the the Go standard library's sort 24 // implementation, found in https://golang.org/src/sort/sort.go. The only 25 // modifications are to template each function into each sort_* struct, so 26 // that the sort methods can be called without incurring any interface overhead 27 // for the Swap and Less methods. 28 29 // maxDepth returns a threshold at which quicksort should switch 30 // to heapsort. It returns 2*ceil(lg(n+1)). 31 func maxDepth(n int) int { 32 var depth int 33 for i := n; i > 0; i >>= 1 { 34 depth++ 35 } 36 return depth * 2 37 } 38 39 // {{range .}} 40 // {{$nulls := .Nulls}} 41 // {{range .DirOverloads}} 42 // {{$dir := .DirString}} 43 // {{range .FamilyOverloads}} 44 // {{range .WidthOverloads}} 45 46 // Insertion sort 47 func (p *sort_TYPE_DIR_HANDLES_NULLSOp) insertionSort(a, b int) { 48 for i := a + 1; i < b; i++ { 49 for j := i; j > a && p.Less(j, j-1); j-- { 50 p.Swap(j, j-1) 51 } 52 } 53 } 54 55 // siftDown implements the heap property on data[lo, hi). 56 // first is an offset into the array where the root of the heap lies. 57 func (p *sort_TYPE_DIR_HANDLES_NULLSOp) siftDown(lo, hi, first int) { 58 root := lo 59 for { 60 child := 2*root + 1 61 if child >= hi { 62 break 63 } 64 if child+1 < hi && p.Less(first+child, first+child+1) { 65 child++ 66 } 67 if !p.Less(first+root, first+child) { 68 return 69 } 70 p.Swap(first+root, first+child) 71 root = child 72 } 73 } 74 75 func (p *sort_TYPE_DIR_HANDLES_NULLSOp) heapSort(ctx context.Context, a, b int) { 76 first := a 77 lo := 0 78 hi := b - a 79 80 // Build heap with greatest element at top. 81 for i := (hi - 1) / 2; i >= 0; i-- { 82 p.cancelChecker.check(ctx) 83 p.siftDown(i, hi, first) 84 } 85 86 // Pop elements, largest first, into end of data. 87 for i := hi - 1; i >= 0; i-- { 88 p.Swap(first, first+i) 89 p.siftDown(lo, i, first) 90 } 91 } 92 93 // Quicksort, loosely following Bentley and McIlroy, 94 // ``Engineering a Sort Function,'' SP&E November 1993. 95 96 // medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. 97 func (p *sort_TYPE_DIR_HANDLES_NULLSOp) medianOfThree(m1, m0, m2 int) { 98 // sort 3 elements 99 if p.Less(m1, m0) { 100 p.Swap(m1, m0) 101 } 102 // data[m0] <= data[m1] 103 if p.Less(m2, m1) { 104 p.Swap(m2, m1) 105 // data[m0] <= data[m2] && data[m1] < data[m2] 106 if p.Less(m1, m0) { 107 p.Swap(m1, m0) 108 } 109 } 110 // now data[m0] <= data[m1] <= data[m2] 111 } 112 113 func (p *sort_TYPE_DIR_HANDLES_NULLSOp) swapRange(a, b, n int) { 114 for i := 0; i < n; i++ { 115 p.Swap(a+i, b+i) 116 } 117 } 118 119 func (p *sort_TYPE_DIR_HANDLES_NULLSOp) doPivot(lo, hi int) (midlo, midhi int) { 120 m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. 121 if hi-lo > 40 { 122 // Tukey's ``Ninther,'' median of three medians of three. 123 s := (hi - lo) / 8 124 p.medianOfThree(lo, lo+s, lo+2*s) 125 p.medianOfThree(m, m-s, m+s) 126 p.medianOfThree(hi-1, hi-1-s, hi-1-2*s) 127 } 128 p.medianOfThree(lo, m, hi-1) 129 130 // Invariants are: 131 // data[lo] = pivot (set up by ChoosePivot) 132 // data[lo < i < a] < pivot 133 // data[a <= i < b] <= pivot 134 // data[b <= i < c] unexamined 135 // data[c <= i < hi-1] > pivot 136 // data[hi-1] >= pivot 137 pivot := lo 138 a, c := lo+1, hi-1 139 140 for ; a < c && p.Less(a, pivot); a++ { 141 } 142 b := a 143 for { 144 for ; b < c && !p.Less(pivot, b); b++ { // data[b] <= pivot 145 } 146 for ; b < c && p.Less(pivot, c-1); c-- { // data[c-1] > pivot 147 } 148 if b >= c { 149 break 150 } 151 // data[b] > pivot; data[c-1] <= pivot 152 p.Swap(b, c-1) 153 b++ 154 c-- 155 } 156 // If hi-c<3 then there are duplicates (by property of median of nine). 157 // Let be a bit more conservative, and set border to 5. 158 protect := hi-c < 5 159 if !protect && hi-c < (hi-lo)/4 { 160 // Lets test some points for equality to pivot 161 dups := 0 162 if !p.Less(pivot, hi-1) { // data[hi-1] = pivot 163 p.Swap(c, hi-1) 164 c++ 165 dups++ 166 } 167 if !p.Less(b-1, pivot) { // data[b-1] = pivot 168 b-- 169 dups++ 170 } 171 // m-lo = (hi-lo)/2 > 6 172 // b-lo > (hi-lo)*3/4-1 > 8 173 // ==> m < b ==> data[m] <= pivot 174 if !p.Less(m, pivot) { // data[m] = pivot 175 p.Swap(m, b-1) 176 b-- 177 dups++ 178 } 179 // if at least 2 points are equal to pivot, assume skewed distribution 180 protect = dups > 1 181 } 182 if protect { 183 // Protect against a lot of duplicates 184 // Add invariant: 185 // data[a <= i < b] unexamined 186 // data[b <= i < c] = pivot 187 for { 188 for ; a < b && !p.Less(b-1, pivot); b-- { // data[b] == pivot 189 } 190 for ; a < b && p.Less(a, pivot); a++ { // data[a] < pivot 191 } 192 if a >= b { 193 break 194 } 195 // data[a] == pivot; data[b-1] < pivot 196 p.Swap(a, b-1) 197 a++ 198 b-- 199 } 200 } 201 // Swap pivot into middle 202 p.Swap(pivot, b-1) 203 return b - 1, c 204 } 205 206 func (p *sort_TYPE_DIR_HANDLES_NULLSOp) quickSort(ctx context.Context, a, b, maxDepth int) { 207 for b-a > 12 { // Use ShellSort for slices <= 12 elements 208 if maxDepth == 0 { 209 p.heapSort(ctx, a, b) 210 return 211 } 212 maxDepth-- 213 p.cancelChecker.check(ctx) 214 mlo, mhi := p.doPivot(a, b) 215 // Avoiding recursion on the larger subproblem guarantees 216 // a stack depth of at most lg(b-a). 217 if mlo-a < b-mhi { 218 p.quickSort(ctx, a, mlo, maxDepth) 219 a = mhi // i.e., quickSort(data, mhi, b) 220 } else { 221 p.quickSort(ctx, mhi, b, maxDepth) 222 b = mlo // i.e., quickSort(data, a, mlo) 223 } 224 } 225 if b-a > 1 { 226 // Do ShellSort pass with gap 6 227 // It could be written in this simplified form cause b-a <= 12 228 for i := a + 6; i < b; i++ { 229 if p.Less(i, i-6) { 230 p.Swap(i, i-6) 231 } 232 } 233 p.insertionSort(a, b) 234 } 235 } 236 237 // {{end}} 238 // {{end}} 239 // {{end}} 240 // {{end}}