go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/tsmon/distribution/distribution.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package distribution contains distribution metrics, fixed width and geometric
    16  // bucketers.
    17  package distribution
    18  
    19  // A Distribution holds a statistical summary of a collection of floating-point
    20  // values.
    21  type Distribution struct {
    22  	b *Bucketer
    23  
    24  	buckets           []int64
    25  	count             int64
    26  	sum               float64
    27  	lastNonZeroBucket int
    28  }
    29  
    30  // New creates a new distribution using the given bucketer.  Passing a nil
    31  // Bucketer will use DefaultBucketer.
    32  func New(b *Bucketer) *Distribution {
    33  	if b == nil {
    34  		b = DefaultBucketer
    35  	}
    36  	return &Distribution{
    37  		b:                 b,
    38  		buckets:           make([]int64, 0),
    39  		lastNonZeroBucket: -1}
    40  }
    41  
    42  // Add adds the sample to the distribution and updates the statistics.
    43  func (d *Distribution) Add(sample float64) {
    44  	i := d.b.Bucket(sample)
    45  	if i >= len(d.buckets) {
    46  		d.buckets = append(d.buckets, make([]int64, i-len(d.buckets)+1)...)
    47  	}
    48  	d.buckets[i]++
    49  	d.sum += sample
    50  	d.count++
    51  	if i > d.lastNonZeroBucket {
    52  		d.lastNonZeroBucket = i
    53  	}
    54  }
    55  
    56  // Bucketer returns the bucketer used in this distribution.
    57  func (d *Distribution) Bucketer() *Bucketer { return d.b }
    58  
    59  // Buckets provides access to the underlying buckets slice.  len(Buckets) will
    60  // be <= Bucketer().NumBuckets()
    61  func (d *Distribution) Buckets() []int64 { return d.buckets }
    62  
    63  // Count returns the number of times Add has been called.
    64  func (d *Distribution) Count() int64 { return d.count }
    65  
    66  // Sum returns the sum of all samples passed to Add.
    67  func (d *Distribution) Sum() float64 { return d.sum }
    68  
    69  // LastNonZeroBucket returns the index into Buckets() of the last bucket that
    70  // is set (non-zero).  Returns -1 if Count() == 0.
    71  func (d *Distribution) LastNonZeroBucket() int { return d.lastNonZeroBucket }
    72  
    73  // Clone creates deep copy of this distribution.
    74  func (d *Distribution) Clone() *Distribution {
    75  	return &Distribution{
    76  		// Bucketer is read-only after init(), and doesn't have a mutable state.
    77  		// No reason to clone Bucketer.
    78  		b:                 d.b,
    79  		buckets:           append(make([]int64, 0, len(d.buckets)), d.buckets...),
    80  		count:             d.count,
    81  		sum:               d.sum,
    82  		lastNonZeroBucket: d.lastNonZeroBucket}
    83  }