github.com/zeebo/mon@v0.0.0-20211012163247-13d39bdb54fa/floathist/histogram.go (about) 1 package floathist 2 3 import ( 4 "math" 5 "sync/atomic" 6 "unsafe" 7 ) 8 9 type ptr = unsafe.Pointer 10 11 const ( 12 levelShift = 5 13 levelSize = 1 << levelShift 14 levelMask = 1<<levelShift - 1 15 ) 16 17 type ( 18 level0 struct { 19 bm b32 20 l1 [levelSize]*level1 21 } 22 level1 struct { 23 bm b32 24 l2 [levelSize]*level2 25 } 26 level2 [levelSize]uint64 27 ) 28 29 type Histogram struct { 30 l0 level0 31 } 32 33 func (h *Histogram) Observe(v float32) { 34 if v != v || v > math.MaxFloat32 || v < -math.MaxFloat32 { 35 return 36 } 37 38 obs := math.Float32bits(v) 39 obs ^= uint32(int32(obs)>>31) | (1 << 31) 40 41 l1i := (obs >> 27) & levelMask 42 l1a := (*ptr)(ptr(&h.l0.l1[l1i])) 43 l1 := (*level1)(atomic.LoadPointer(l1a)) 44 if l1 == nil { 45 l1 = new(level1) 46 if !atomic.CompareAndSwapPointer(l1a, nil, ptr(l1)) { 47 l1 = (*level1)(atomic.LoadPointer(l1a)) 48 } else { 49 h.l0.bm.Set(uint(l1i)) 50 } 51 } 52 53 l2i := (obs >> 22) & levelMask 54 l2a := (*ptr)(ptr(&l1.l2[l2i])) 55 l2 := (*level2)(atomic.LoadPointer(l2a)) 56 if l2 == nil { 57 l2 = new(level2) 58 if !atomic.CompareAndSwapPointer(l2a, nil, ptr(l2)) { 59 l2 = (*level2)(atomic.LoadPointer(l2a)) 60 } else { 61 l1.bm.Set(uint(l2i)) 62 } 63 } 64 65 atomic.AddUint64(&l2[(obs>>17)&levelMask], 1) 66 } 67 68 func (h *Histogram) Total() (total int64) { 69 bm := h.l0.bm.Clone() 70 for { 71 i, ok := bm.Next() 72 if !ok { 73 break 74 } 75 l1 := (*level1)(atomic.LoadPointer((*ptr)(ptr(&h.l0.l1[i])))) 76 77 bm := l1.bm.Clone() 78 for { 79 i, ok := bm.Next() 80 if !ok { 81 break 82 } 83 l2 := (*level2)(atomic.LoadPointer((*ptr)(ptr(&l1.l2[i])))) 84 85 for i := 0; i < levelSize; i++ { 86 total += int64(atomic.LoadUint64(&l2[i])) 87 } 88 } 89 } 90 91 return total 92 } 93 94 func (h *Histogram) Quantile(q float64) float32 { 95 target, acc := uint64(q*float64(h.Total())+0.5), uint64(0) 96 97 bm := h.l0.bm.Clone() 98 for { 99 i, ok := bm.Next() 100 if !ok { 101 break 102 } 103 l1 := (*level1)(atomic.LoadPointer((*ptr)(ptr(&h.l0.l1[i])))) 104 105 bm := l1.bm.Clone() 106 for { 107 j, ok := bm.Next() 108 if !ok { 109 break 110 } 111 l2 := (*level2)(atomic.LoadPointer((*ptr)(ptr(&l1.l2[j])))) 112 113 for k := uint32(0); k < levelSize; k++ { 114 acc += atomic.LoadUint64(&l2[k]) 115 if acc >= target { 116 obs := i<<27 | j<<22 | k<<17 117 obs ^= ^uint32(int32(obs)>>31) | (1 << 31) 118 return math.Float32frombits(obs) 119 } 120 } 121 } 122 } 123 124 return math.Float32frombits((1<<15 - 1) << 17) 125 } 126 127 func (h *Histogram) CDF(v float32) float64 { 128 obs := math.Float32bits(v) 129 obs ^= uint32(int32(obs)>>31) | (1 << 31) 130 131 var sum, total uint64 132 133 bm := h.l0.bm.Clone() 134 for { 135 i, ok := bm.Next() 136 if !ok { 137 break 138 } 139 l1 := (*level1)(atomic.LoadPointer((*ptr)(ptr(&h.l0.l1[i])))) 140 141 bm := l1.bm.Clone() 142 for { 143 j, ok := bm.Next() 144 if !ok { 145 break 146 } 147 l2 := (*level2)(atomic.LoadPointer((*ptr)(ptr(&l1.l2[j])))) 148 149 target := i<<27 | j<<22 150 151 for k := uint32(0); k < levelSize; k++ { 152 count := atomic.LoadUint64(&l2[k]) 153 if obs >= target { 154 sum += count 155 target += 1 << 17 156 } 157 total += count 158 } 159 } 160 } 161 162 return float64(sum) / float64(total) 163 } 164 165 func (h *Histogram) Sum() (sum float64) { 166 bm := h.l0.bm.Clone() 167 for { 168 i, ok := bm.Next() 169 if !ok { 170 break 171 } 172 l1 := (*level1)(atomic.LoadPointer((*ptr)(ptr(&h.l0.l1[i])))) 173 174 bm := l1.bm.Clone() 175 for { 176 j, ok := bm.Next() 177 if !ok { 178 break 179 } 180 l2 := (*level2)(atomic.LoadPointer((*ptr)(ptr(&l1.l2[j])))) 181 182 for k := uint32(0); k < levelSize; k++ { 183 count := float64(atomic.LoadUint64(&l2[k])) 184 obs := i<<27 | j<<22 | k<<17 | 1<<16 185 obs ^= ^uint32(int32(obs)>>31) | (1 << 31) 186 value := float64(math.Float32frombits(obs)) 187 188 sum += count * value 189 } 190 } 191 } 192 193 return sum 194 } 195 196 func (h *Histogram) Average() (sum, avg float64) { 197 var total float64 198 199 bm := h.l0.bm.Clone() 200 for { 201 i, ok := bm.Next() 202 if !ok { 203 break 204 } 205 l1 := (*level1)(atomic.LoadPointer((*ptr)(ptr(&h.l0.l1[i])))) 206 207 bm := l1.bm.Clone() 208 for { 209 j, ok := bm.Next() 210 if !ok { 211 break 212 } 213 l2 := (*level2)(atomic.LoadPointer((*ptr)(ptr(&l1.l2[j])))) 214 215 for k := uint32(0); k < levelSize; k++ { 216 count := float64(atomic.LoadUint64(&l2[k])) 217 obs := i<<27 | j<<22 | k<<17 | 1<<16 218 obs ^= ^uint32(int32(obs)>>31) | (1 << 31) 219 value := float64(math.Float32frombits(obs)) 220 221 total += count 222 sum += count * value 223 } 224 } 225 } 226 227 if total == 0 { 228 return 0, 0 229 } 230 return sum, sum / total 231 } 232 233 func (h *Histogram) Variance() (sum, avg, vari float64) { 234 var total, total2 float64 235 236 bm := h.l0.bm.Clone() 237 for { 238 i, ok := bm.Next() 239 if !ok { 240 break 241 } 242 l1 := (*level1)(atomic.LoadPointer((*ptr)(ptr(&h.l0.l1[i])))) 243 244 bm := l1.bm.Clone() 245 for { 246 j, ok := bm.Next() 247 if !ok { 248 break 249 } 250 l2 := (*level2)(atomic.LoadPointer((*ptr)(ptr(&l1.l2[j])))) 251 252 for k := uint32(0); k < levelSize; k++ { 253 count := float64(atomic.LoadUint64(&l2[k])) 254 obs := i<<27 | j<<22 | k<<17 | 1<<16 255 obs ^= ^uint32(int32(obs)>>31) | (1 << 31) 256 value := float64(math.Float32frombits(obs)) 257 258 total += count 259 total2 += count * count 260 avg_ := avg 261 avg += (count / total) * (value - avg_) 262 sum += count * value 263 vari += count * (value - avg_) * (value - avg) 264 } 265 } 266 } 267 268 if total == 0 { 269 return 0, 0, 0 270 } else if total == 1 { 271 return sum, sum / total, 0 272 } 273 return sum, sum / total, vari / (total - 1) 274 }