github.com/anycable/anycable-go@v1.5.1/stats/stats.go (about) 1 // Package stats contains calculation utils for benchmarks 2 // Based on https://github.com/anycable/websocket-bench/blob/master/benchmark/stat.go 3 package stats 4 5 import ( 6 "sort" 7 "time" 8 ) 9 10 // RoundToMS returns the number of milliseconds for the given duration 11 func RoundToMS(d time.Duration) int64 { 12 return int64((d + (500 * time.Microsecond)) / time.Millisecond) 13 } 14 15 // ResAggregate contains duration samples 16 type ResAggregate struct { 17 samples []time.Duration 18 sorted bool 19 } 20 21 type byAsc []time.Duration 22 23 func (a byAsc) Len() int { return len(a) } 24 func (a byAsc) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 25 func (a byAsc) Less(i, j int) bool { return a[i] < a[j] } 26 27 // Add adds a new sample to the aggregate 28 func (agg *ResAggregate) Add(rtt time.Duration) { 29 agg.samples = append(agg.samples, rtt) 30 agg.sorted = false 31 } 32 33 // Count returns the number of samples 34 func (agg *ResAggregate) Count() int { 35 return len(agg.samples) 36 } 37 38 // Min returns the min value 39 func (agg *ResAggregate) Min() time.Duration { 40 if agg.Count() == 0 { 41 return 0 42 } 43 agg.sort() 44 return agg.samples[0] 45 } 46 47 // Max returns the max value 48 func (agg *ResAggregate) Max() time.Duration { 49 if agg.Count() == 0 { 50 return 0 51 } 52 agg.sort() 53 return agg.samples[len(agg.samples)-1] 54 } 55 56 // Percentile returns the p-th percentile 57 func (agg *ResAggregate) Percentile(p int) time.Duration { 58 if p <= 0 { 59 panic("p must be greater than 0") 60 } else if 100 <= p { 61 panic("p must be less 100") 62 } 63 64 agg.sort() 65 66 rank := p * len(agg.samples) / 100 67 68 if agg.Count() == 0 { 69 return 0 70 } 71 72 return agg.samples[rank] 73 } 74 75 func (agg *ResAggregate) sort() { 76 if agg.sorted { 77 return 78 } 79 sort.Sort(byAsc(agg.samples)) 80 agg.sorted = true 81 }