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  }