github.com/zeebo/mon@v0.0.0-20211012163247-13d39bdb54fa/inthist/histogram_test.go (about) 1 package inthist 2 3 import ( 4 "encoding/binary" 5 "encoding/hex" 6 "sync/atomic" 7 "testing" 8 9 "github.com/zeebo/assert" 10 "github.com/zeebo/pcg" 11 ) 12 13 func TestHistogram(t *testing.T) { 14 t.Run("Walk", func(t *testing.T) { 15 type key = [2]uint64 16 17 var ( 18 bucket uint64 19 entry uint64 20 value int64 21 22 bucketEntries = map[key]bool{} 23 ) 24 25 for bucket < histBuckets && entry < histEntries { 26 // we must be on a new bucket/entry combination 27 assert.That(t, !bucketEntries[key{bucket, entry}]) 28 bucketEntries[key{bucket, entry}] = true 29 30 // value is always lowerValue(bucket, entry) 31 assert.Equal(t, value, lowerValue(bucket, entry)) 32 33 // bucketEntry(lowerValue(bucket, entry)) == bucket, entry 34 lbucket, lentry := bucketEntry(lowerValue(bucket, entry)) 35 assert.Equal(t, bucket, uint(lbucket)) 36 assert.Equal(t, entry, int(lentry)) 37 38 // bucketEntry(upperValue(bucket, entry)) == bucket, entry 39 ubucket, uentry := bucketEntry(upperValue(bucket, entry)) 40 assert.Equal(t, bucket, uint(ubucket)) 41 assert.Equal(t, entry, int(uentry)) 42 43 // upperValue(bucket, entry) + 1 is in the next bucket/entry 44 value = upperValue(bucket, entry) + 1 45 bucket, entry = bucketEntry(value) 46 } 47 48 // we must have hit every bucket/entry 49 assert.Equal(t, len(bucketEntries), histBuckets*histEntries) 50 }) 51 52 t.Run("Zero", func(t *testing.T) { 53 h := new(Histogram) 54 sum, average, variance := h.Variance() 55 assert.Equal(t, sum, 0.0) 56 assert.Equal(t, average, 0.0) 57 assert.Equal(t, variance, 0.0) 58 }) 59 60 t.Run("Boundaries", func(t *testing.T) { 61 h := new(Histogram) 62 63 h.Observe(0) 64 assert.Equal(t, h.Total(), 1) 65 66 h.Observe(-1) 67 assert.Equal(t, h.Total(), 1) 68 69 upper := upperValue(histBuckets-1, histEntries-1) 70 71 h.Observe(upper) 72 assert.Equal(t, h.Total(), 2) 73 74 for upper++; upper > 0; upper++ { 75 h.Observe(upper) 76 assert.Equal(t, h.Total(), 2) 77 } 78 }) 79 80 t.Run("Basic", func(t *testing.T) { 81 h := new(Histogram) 82 83 for i := int64(0); i < 1000; i++ { 84 h.Observe(i) 85 } 86 }) 87 88 t.Run("Quantile", func(t *testing.T) { 89 h := new(Histogram) 90 for i := int64(0); i < 1000; i++ { 91 h.Observe(i) 92 } 93 94 assert.Equal(t, h.Quantile(0), 0) 95 assert.Equal(t, h.Quantile(.25), 250) 96 assert.Equal(t, h.Quantile(.5), 500) 97 assert.Equal(t, h.Quantile(1), 1000) 98 }) 99 100 t.Run("CDF", func(t *testing.T) { 101 h := new(Histogram) 102 for i := int64(0); i < 1000; i++ { 103 h.Observe(i) 104 } 105 106 assert.Equal(t, h.CDF(0), 0.001) 107 assert.Equal(t, h.CDF(250), 0.252) 108 assert.Equal(t, h.CDF(500), 0.504) 109 assert.Equal(t, h.CDF(1000), 1.0) 110 }) 111 112 t.Run("Variance", func(t *testing.T) { 113 h := new(Histogram) 114 rsum := int64(0) 115 for i := int64(0); i < 1000; i++ { 116 h.Observe(i) 117 rsum += i 118 } 119 120 sum, average, variance := h.Variance() 121 122 assert.Equal(t, sum, 499532.0) 123 assert.Equal(t, average, 499.532) 124 assert.Equal(t, variance, 83361.358976) 125 }) 126 127 t.Run("Percentiles", func(t *testing.T) { 128 h := new(Histogram) 129 for i := int64(0); i < 1000; i++ { 130 r := int64(pcg.Uint32n(1000)) 131 h.Observe(r * r) 132 } 133 134 h.Percentiles(func(value, count, total int64) { 135 t.Log(value, count, total) 136 }) 137 }) 138 139 t.Run("Serialize", func(t *testing.T) { 140 h := new(Histogram) 141 for i := int64(0); i < 10000; i++ { 142 r := int64(pcg.Uint32n(100) + 500) 143 h.Observe(r) 144 } 145 h.Observe(1) 146 h.Observe(3) 147 h.Observe(5) 148 149 data := h.Serialize(nil) 150 t.Logf("%d\n%s", len(data), hex.Dump(data)) 151 t.Logf("%064b\n", binary.LittleEndian.Uint64(data[:8])) 152 }) 153 154 t.Run("Load", func(t *testing.T) { 155 h := new(Histogram) 156 for i := int64(0); i < 10000; i++ { 157 r := int64(pcg.Uint32n(1000) + 500) 158 h.Observe(r) 159 } 160 161 h2 := new(Histogram) 162 assert.NoError(t, h2.Load(h.Serialize(nil))) 163 164 assert.Equal(t, h.Total(), h2.Total()) 165 assert.Equal(t, h.Sum(), h2.Sum()) 166 t.Log(h.Average()) 167 t.Log(h2.Average()) 168 }) 169 } 170 171 func BenchmarkHistogram(b *testing.B) { 172 b.Run("SumHistogramSlow", func(b *testing.B) { 173 var buf [64]uint32 174 175 for i := 0; i < b.N; i++ { 176 _ = sumHistogramSlow(&buf) 177 } 178 }) 179 180 b.Run("SumHistogram", func(b *testing.B) { 181 var buf [64]uint32 182 183 for i := 0; i < b.N; i++ { 184 _ = sumHistogram(&buf) 185 } 186 }) 187 188 b.Run("Observe", func(b *testing.B) { 189 his := new(Histogram) 190 191 for i := 0; i < b.N; i++ { 192 his.Observe(1) 193 } 194 }) 195 196 b.Run("Observe_Parallel", func(b *testing.B) { 197 his := new(Histogram) 198 n := int64(0) 199 b.RunParallel(func(pb *testing.PB) { 200 i := int64(1024) << uint64(atomic.AddInt64(&n, 1)) 201 for pb.Next() { 202 his.Observe(i) 203 } 204 }) 205 }) 206 207 b.Run("Total", func(b *testing.B) { 208 his := new(Histogram) 209 for i := 0; i < 1000000; i++ { 210 his.Observe(int64(pcg.Uint64() >> histEntriesBits)) 211 } 212 b.ReportAllocs() 213 b.ResetTimer() 214 215 for i := 0; i < b.N; i++ { 216 his.Total() 217 } 218 }) 219 220 b.Run("Total_Easy", func(b *testing.B) { 221 his := new(Histogram) 222 for i := 0; i < 1000000; i++ { 223 his.Observe(int64(pcg.Uint32n(64))) 224 } 225 b.ReportAllocs() 226 b.ResetTimer() 227 228 for i := 0; i < b.N; i++ { 229 his.Total() 230 } 231 }) 232 233 b.Run("Quantile", func(b *testing.B) { 234 his := new(Histogram) 235 for i := 0; i < 1000000; i++ { 236 his.Observe(int64(pcg.Uint64() >> histEntriesBits)) 237 } 238 assert.Equal(b, his.Total(), 1000000) 239 b.ReportAllocs() 240 b.ResetTimer() 241 242 for i := 0; i < b.N; i++ { 243 his.Quantile(pcg.Float64()) 244 } 245 }) 246 247 b.Run("Quantile_Easy", func(b *testing.B) { 248 his := new(Histogram) 249 for i := 0; i < 1000000; i++ { 250 his.Observe(int64(pcg.Uint32n(64))) 251 } 252 assert.Equal(b, his.Total(), 1000000) 253 b.ReportAllocs() 254 b.ResetTimer() 255 256 for i := 0; i < b.N; i++ { 257 his.Quantile(pcg.Float64()) 258 } 259 }) 260 261 b.Run("CDF", func(b *testing.B) { 262 his := new(Histogram) 263 for i := 0; i < 1000000; i++ { 264 his.Observe(int64(pcg.Uint64() >> histEntriesBits)) 265 } 266 assert.Equal(b, his.Total(), 1000000) 267 b.ReportAllocs() 268 b.ResetTimer() 269 270 for i := 0; i < b.N; i++ { 271 his.CDF(int64(pcg.Uint64() >> histEntriesBits)) 272 } 273 }) 274 275 b.Run("CDF_Easy", func(b *testing.B) { 276 his := new(Histogram) 277 for i := 0; i < 1000000; i++ { 278 his.Observe(int64(pcg.Uint32n(64))) 279 } 280 assert.Equal(b, his.Total(), 1000000) 281 b.ReportAllocs() 282 b.ResetTimer() 283 284 for i := 0; i < b.N; i++ { 285 his.CDF(int64(pcg.Uint32n(64))) 286 } 287 }) 288 289 b.Run("Sum", func(b *testing.B) { 290 his := new(Histogram) 291 for i := 0; i < 1000; i++ { 292 his.Observe(int64(pcg.Uint32n(64))) 293 } 294 assert.Equal(b, his.Total(), 1000) 295 b.ReportAllocs() 296 b.ResetTimer() 297 298 for i := 0; i < b.N; i++ { 299 _ = his.Sum() 300 } 301 }) 302 303 b.Run("Average", func(b *testing.B) { 304 his := new(Histogram) 305 for i := 0; i < 1000; i++ { 306 his.Observe(int64(pcg.Uint32n(64))) 307 } 308 assert.Equal(b, his.Total(), 1000) 309 b.ReportAllocs() 310 b.ResetTimer() 311 312 for i := 0; i < b.N; i++ { 313 _, _ = his.Average() 314 } 315 }) 316 317 b.Run("Variance", func(b *testing.B) { 318 his := new(Histogram) 319 for i := 0; i < 1000; i++ { 320 his.Observe(int64(pcg.Uint32n(64))) 321 } 322 assert.Equal(b, his.Total(), 1000) 323 b.ReportAllocs() 324 b.ResetTimer() 325 326 for i := 0; i < b.N; i++ { 327 _, _, _ = his.Variance() 328 } 329 }) 330 331 b.Run("Serialize", func(b *testing.B) { 332 h := new(Histogram) 333 for i := int64(0); i < 10000000; i++ { 334 r := int64(pcg.Uint32n(1000) + 500) 335 h.Observe(r) 336 } 337 buf := h.Serialize(nil) 338 339 b.SetBytes(int64(len(buf))) 340 b.ResetTimer() 341 b.ReportAllocs() 342 343 for i := 0; i < b.N; i++ { 344 h.Serialize(buf[:0]) 345 } 346 347 b.ReportMetric(float64(len(buf)), "bytes") 348 }) 349 350 b.Run("Load", func(b *testing.B) { 351 h := new(Histogram) 352 for i := int64(0); i < 10000000; i++ { 353 r := int64(pcg.Uint32n(1000) + 500) 354 h.Observe(r) 355 } 356 buf := h.Serialize(nil) 357 358 b.SetBytes(int64(len(buf))) 359 b.ResetTimer() 360 b.ReportAllocs() 361 362 for i := 0; i < b.N; i++ { 363 var h Histogram 364 _ = h.Load(buf) 365 } 366 367 b.ReportMetric(float64(len(buf)), "bytes") 368 }) 369 }