184 lines
6.4 KiB
Go
184 lines
6.4 KiB
Go
package congestion
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
|
)
|
|
|
|
const (
|
|
// Note: This constant is also defined in the ackhandler package.
|
|
initialRTTus = 100 * 1000
|
|
rttAlpha float32 = 0.125
|
|
oneMinusAlpha float32 = (1 - rttAlpha)
|
|
rttBeta float32 = 0.25
|
|
oneMinusBeta float32 = (1 - rttBeta)
|
|
halfWindow float32 = 0.5
|
|
quarterWindow float32 = 0.25
|
|
)
|
|
|
|
type rttSample struct {
|
|
rtt time.Duration
|
|
time time.Time
|
|
}
|
|
|
|
// RTTStats provides round-trip statistics
|
|
type RTTStats struct {
|
|
initialRTTus int64
|
|
|
|
recentMinRTTwindow time.Duration
|
|
minRTT time.Duration
|
|
latestRTT time.Duration
|
|
smoothedRTT time.Duration
|
|
meanDeviation time.Duration
|
|
|
|
numMinRTTsamplesRemaining uint32
|
|
|
|
newMinRTT rttSample
|
|
recentMinRTT rttSample
|
|
halfWindowRTT rttSample
|
|
quarterWindowRTT rttSample
|
|
}
|
|
|
|
// NewRTTStats makes a properly initialized RTTStats object
|
|
func NewRTTStats() *RTTStats {
|
|
return &RTTStats{
|
|
initialRTTus: initialRTTus,
|
|
recentMinRTTwindow: utils.InfDuration,
|
|
}
|
|
}
|
|
|
|
// InitialRTTus is the initial RTT in us
|
|
func (r *RTTStats) InitialRTTus() int64 { return r.initialRTTus }
|
|
|
|
// MinRTT Returns the minRTT for the entire connection.
|
|
// May return Zero if no valid updates have occurred.
|
|
func (r *RTTStats) MinRTT() time.Duration { return r.minRTT }
|
|
|
|
// LatestRTT returns the most recent rtt measurement.
|
|
// May return Zero if no valid updates have occurred.
|
|
func (r *RTTStats) LatestRTT() time.Duration { return r.latestRTT }
|
|
|
|
// RecentMinRTT the minRTT since SampleNewRecentMinRtt has been called, or the
|
|
// minRTT for the entire connection if SampleNewMinRtt was never called.
|
|
func (r *RTTStats) RecentMinRTT() time.Duration { return r.recentMinRTT.rtt }
|
|
|
|
// SmoothedRTT returns the EWMA smoothed RTT for the connection.
|
|
// May return Zero if no valid updates have occurred.
|
|
func (r *RTTStats) SmoothedRTT() time.Duration { return r.smoothedRTT }
|
|
|
|
// GetQuarterWindowRTT gets the quarter window RTT
|
|
func (r *RTTStats) GetQuarterWindowRTT() time.Duration { return r.quarterWindowRTT.rtt }
|
|
|
|
// GetHalfWindowRTT gets the half window RTT
|
|
func (r *RTTStats) GetHalfWindowRTT() time.Duration { return r.halfWindowRTT.rtt }
|
|
|
|
// MeanDeviation gets the mean deviation
|
|
func (r *RTTStats) MeanDeviation() time.Duration { return r.meanDeviation }
|
|
|
|
// SetRecentMinRTTwindow sets how old a recent min rtt sample can be.
|
|
func (r *RTTStats) SetRecentMinRTTwindow(recentMinRTTwindow time.Duration) {
|
|
r.recentMinRTTwindow = recentMinRTTwindow
|
|
}
|
|
|
|
// UpdateRTT updates the RTT based on a new sample.
|
|
func (r *RTTStats) UpdateRTT(sendDelta, ackDelay time.Duration, now time.Time) {
|
|
if sendDelta == utils.InfDuration || sendDelta <= 0 {
|
|
utils.Debugf("Ignoring measured sendDelta, because it's is either infinite, zero, or negative: %d", sendDelta/time.Microsecond)
|
|
return
|
|
}
|
|
|
|
// Update r.minRTT first. r.minRTT does not use an rttSample corrected for
|
|
// ackDelay but the raw observed sendDelta, since poor clock granularity at
|
|
// the client may cause a high ackDelay to result in underestimation of the
|
|
// r.minRTT.
|
|
if r.minRTT == 0 || r.minRTT > sendDelta {
|
|
r.minRTT = sendDelta
|
|
}
|
|
r.updateRecentMinRTT(sendDelta, now)
|
|
|
|
// Correct for ackDelay if information received from the peer results in a
|
|
// positive RTT sample. Otherwise, we use the sendDelta as a reasonable
|
|
// measure for smoothedRTT.
|
|
sample := sendDelta
|
|
if sample > ackDelay {
|
|
sample -= ackDelay
|
|
}
|
|
r.latestRTT = sample
|
|
// First time call.
|
|
if r.smoothedRTT == 0 {
|
|
r.smoothedRTT = sample
|
|
r.meanDeviation = sample / 2
|
|
} else {
|
|
r.meanDeviation = time.Duration(oneMinusBeta*float32(r.meanDeviation/time.Microsecond)+rttBeta*float32(utils.AbsDuration(r.smoothedRTT-sample)/time.Microsecond)) * time.Microsecond
|
|
r.smoothedRTT = time.Duration((float32(r.smoothedRTT/time.Microsecond)*oneMinusAlpha)+(float32(sample/time.Microsecond)*rttAlpha)) * time.Microsecond
|
|
}
|
|
}
|
|
|
|
func (r *RTTStats) updateRecentMinRTT(sample time.Duration, now time.Time) { // Recent minRTT update.
|
|
if r.numMinRTTsamplesRemaining > 0 {
|
|
r.numMinRTTsamplesRemaining--
|
|
if r.newMinRTT.rtt == 0 || sample <= r.newMinRTT.rtt {
|
|
r.newMinRTT = rttSample{rtt: sample, time: now}
|
|
}
|
|
if r.numMinRTTsamplesRemaining == 0 {
|
|
r.recentMinRTT = r.newMinRTT
|
|
r.halfWindowRTT = r.newMinRTT
|
|
r.quarterWindowRTT = r.newMinRTT
|
|
}
|
|
}
|
|
|
|
// Update the three recent rtt samples.
|
|
if r.recentMinRTT.rtt == 0 || sample <= r.recentMinRTT.rtt {
|
|
r.recentMinRTT = rttSample{rtt: sample, time: now}
|
|
r.halfWindowRTT = r.recentMinRTT
|
|
r.quarterWindowRTT = r.recentMinRTT
|
|
} else if sample <= r.halfWindowRTT.rtt {
|
|
r.halfWindowRTT = rttSample{rtt: sample, time: now}
|
|
r.quarterWindowRTT = r.halfWindowRTT
|
|
} else if sample <= r.quarterWindowRTT.rtt {
|
|
r.quarterWindowRTT = rttSample{rtt: sample, time: now}
|
|
}
|
|
|
|
// Expire old min rtt samples.
|
|
if r.recentMinRTT.time.Before(now.Add(-r.recentMinRTTwindow)) {
|
|
r.recentMinRTT = r.halfWindowRTT
|
|
r.halfWindowRTT = r.quarterWindowRTT
|
|
r.quarterWindowRTT = rttSample{rtt: sample, time: now}
|
|
} else if r.halfWindowRTT.time.Before(now.Add(-time.Duration(float32(r.recentMinRTTwindow/time.Microsecond)*halfWindow) * time.Microsecond)) {
|
|
r.halfWindowRTT = r.quarterWindowRTT
|
|
r.quarterWindowRTT = rttSample{rtt: sample, time: now}
|
|
} else if r.quarterWindowRTT.time.Before(now.Add(-time.Duration(float32(r.recentMinRTTwindow/time.Microsecond)*quarterWindow) * time.Microsecond)) {
|
|
r.quarterWindowRTT = rttSample{rtt: sample, time: now}
|
|
}
|
|
}
|
|
|
|
// SampleNewRecentMinRTT forces RttStats to sample a new recent min rtt within the next
|
|
// |numSamples| UpdateRTT calls.
|
|
func (r *RTTStats) SampleNewRecentMinRTT(numSamples uint32) {
|
|
r.numMinRTTsamplesRemaining = numSamples
|
|
r.newMinRTT = rttSample{}
|
|
}
|
|
|
|
// OnConnectionMigration is called when connection migrates and rtt measurement needs to be reset.
|
|
func (r *RTTStats) OnConnectionMigration() {
|
|
r.latestRTT = 0
|
|
r.minRTT = 0
|
|
r.smoothedRTT = 0
|
|
r.meanDeviation = 0
|
|
r.initialRTTus = initialRTTus
|
|
r.numMinRTTsamplesRemaining = 0
|
|
r.recentMinRTTwindow = utils.InfDuration
|
|
r.recentMinRTT = rttSample{}
|
|
r.halfWindowRTT = rttSample{}
|
|
r.quarterWindowRTT = rttSample{}
|
|
}
|
|
|
|
// ExpireSmoothedMetrics causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt
|
|
// is larger. The mean deviation is increased to the most recent deviation if
|
|
// it's larger.
|
|
func (r *RTTStats) ExpireSmoothedMetrics() {
|
|
r.meanDeviation = utils.MaxDuration(r.meanDeviation, utils.AbsDuration(r.smoothedRTT-r.latestRTT))
|
|
r.smoothedRTT = utils.MaxDuration(r.smoothedRTT, r.latestRTT)
|
|
}
|