github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/aggregator/aggregation/quantile/cm/heap.go (about) 1 // Copyright (c) 2016 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 cm 22 23 // minHeap is a typed min heap for floating point numbers. Unlike the generic 24 // heap in the container/heap package, pushing data to or popping data off of 25 // the heap doesn't require conversion between floats and interface{} objects, 26 // therefore avoiding the memory and GC overhead due to the additional allocations. 27 type minHeap []float64 28 29 // Len returns the number of values in the heap. 30 func (h minHeap) Len() int { return len(h) } 31 32 // Min returns the minimum value from the heap. 33 func (h minHeap) Min() float64 { return h[0] } 34 35 // Push pushes a value onto the heap. 36 func (h *minHeap) Push(value float64) { 37 if len(*h) == cap(*h) { 38 h.ensureSize() 39 } 40 // append 41 (*h) = append(*h, value) 42 // then, shift up if necessary to fix heap structure. manually inlined. 43 heap := *h 44 n := len(heap) 45 i := n - 1 46 for i < n && i >= 0 { 47 parent := (i - 1) / 2 48 if parent == i || parent >= n || parent < 0 || heap[parent] <= heap[i] { 49 break 50 } 51 heap[parent], heap[i] = heap[i], heap[parent] 52 i = parent 53 } 54 } 55 56 func (h *minHeap) Reset() { 57 if heap := *h; cap(heap) >= _initialHeapBucketSize { 58 sharedHeapPool.Put(heap) 59 } 60 (*h) = nil 61 } 62 63 // Pop pops the minimum value from the heap. 64 func (h *minHeap) Pop() float64 { 65 var ( 66 old = *h 67 n = len(old) - 1 68 val = old[0] 69 i int 70 ) 71 old[0], old[n] = old[n], old[0] 72 smallest := i 73 for smallest >= 0 && smallest <= n { // bounds-check elimination hint 74 left := smallest*2 + 1 75 right := left + 1 76 if left < n && left >= 0 && old[left] < old[smallest] { 77 smallest = left 78 } 79 if right < n && right >= 0 && old[right] < old[smallest] { 80 smallest = right 81 } 82 if smallest == i { 83 break 84 } 85 old[i], old[smallest] = old[smallest], old[i] 86 i = smallest 87 } 88 *h = old[0:n] 89 return val 90 } 91 92 func (h minHeap) SortDesc() { 93 heap := h 94 // this is equivalent to Pop() in a loop (heapsort) 95 // all the redundant-looking conditions are there to eliminate bounds-checks 96 for n := len(heap) - 1; n > 0 && n < len(heap); n = len(heap) - 1 { 97 var ( 98 i int 99 smallest int 100 ) 101 heap[0], heap[n] = heap[n], heap[0] 102 for smallest >= 0 && smallest <= n { 103 var ( 104 left = smallest*2 + 1 105 right = left + 1 106 ) 107 if left < n && left >= 0 && heap[left] < heap[smallest] { 108 smallest = left 109 } 110 if right < n && right >= 0 && heap[right] < heap[smallest] { 111 smallest = right 112 } 113 if smallest == i { 114 break 115 } 116 heap[i], heap[smallest] = heap[smallest], heap[i] 117 i = smallest 118 } 119 heap = heap[0:n] 120 } 121 } 122 123 func (h *minHeap) ensureSize() { 124 var ( 125 heap = *h 126 targetCap = cap(heap) * 2 127 newHeap = sharedHeapPool.Get(targetCap) 128 ) 129 (*newHeap) = append(*newHeap, heap...) 130 if cap(heap) >= _initialHeapBucketSize { 131 sharedHeapPool.Put(heap) 132 } 133 (*h) = *newHeap 134 }