github.com/livekit/protocol@v1.39.3/utils/latencyaggregate.go (about)

     1  // Copyright 2023 LiveKit, Inc.
     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 utils
    16  
    17  import (
    18  	"time"
    19  
    20  	"go.uber.org/zap/zapcore"
    21  )
    22  
    23  // a ring buffer of welford mean/var summaries used to aggregate jitter and rtt.
    24  type LatencyAggregate struct {
    25  	summary []Welford
    26  	ivl     time.Duration
    27  	cap     uint64
    28  	head    uint64
    29  }
    30  
    31  func NewLatencyAggregate(interval, windowLength time.Duration) *LatencyAggregate {
    32  	c := uint64((windowLength + interval - 1) / interval)
    33  	return &LatencyAggregate{
    34  		summary: make([]Welford, c),
    35  		ivl:     interval,
    36  		cap:     uint64(c),
    37  	}
    38  }
    39  
    40  // extend the ring to contain ts then merge the value into the interval summary.
    41  func (a *LatencyAggregate) Update(ts time.Duration, v float64) {
    42  	i := uint64(ts / a.ivl)
    43  	if i+a.cap < a.head {
    44  		return
    45  	}
    46  
    47  	if i > a.head {
    48  		k := a.head + 1
    49  		if k+a.cap < i {
    50  			k = i - a.cap
    51  		}
    52  		for ; k <= i; k++ {
    53  			a.summary[k%a.cap].Reset()
    54  		}
    55  		a.head = i
    56  	}
    57  
    58  	a.summary[i%a.cap].Update(v)
    59  }
    60  
    61  func (a *LatencyAggregate) Get(ts time.Duration) (Welford, bool) {
    62  	i := uint64(ts / a.ivl)
    63  	if i+a.cap < a.head || i > a.head {
    64  		return Welford{}, false
    65  	}
    66  	return a.summary[i%a.cap], true
    67  }
    68  
    69  // aggregate the interval summaries
    70  func (a *LatencyAggregate) Summarize() Welford {
    71  	return WelfordMerge(a.summary...)
    72  }
    73  
    74  func (a *LatencyAggregate) SummarizeLast(d time.Duration) Welford {
    75  	n := min(a.head, a.cap, uint64((d+a.ivl-1)/a.ivl))
    76  	l := (a.head - n) % a.cap
    77  	r := (a.head % a.cap)
    78  	if l < r {
    79  		return WelfordMerge(a.summary[l:r]...)
    80  	}
    81  	return WelfordMerge(
    82  		WelfordMerge(a.summary[:r]...),
    83  		WelfordMerge(a.summary[l:]...),
    84  	)
    85  }
    86  
    87  func (a *LatencyAggregate) MarshalLogObject(e zapcore.ObjectEncoder) error {
    88  	summary := a.Summarize()
    89  	e.AddFloat64("count", summary.Count())
    90  	e.AddFloat64("mean", summary.Mean())
    91  	e.AddFloat64("stddev", summary.StdDev())
    92  	return nil
    93  }