github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/internal/utils/rtt_stats.go (about)

     1  package utils
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/apernet/quic-go/internal/protocol"
     7  )
     8  
     9  const (
    10  	rttAlpha      = 0.125
    11  	oneMinusAlpha = 1 - rttAlpha
    12  	rttBeta       = 0.25
    13  	oneMinusBeta  = 1 - rttBeta
    14  	// The default RTT used before an RTT sample is taken.
    15  	defaultInitialRTT = 100 * time.Millisecond
    16  )
    17  
    18  // RTTStats provides round-trip statistics
    19  type RTTStats struct {
    20  	hasMeasurement bool
    21  
    22  	minRTT        time.Duration
    23  	latestRTT     time.Duration
    24  	smoothedRTT   time.Duration
    25  	meanDeviation time.Duration
    26  
    27  	maxAckDelay time.Duration
    28  }
    29  
    30  // NewRTTStats makes a properly initialized RTTStats object
    31  func NewRTTStats() *RTTStats {
    32  	return &RTTStats{}
    33  }
    34  
    35  // MinRTT Returns the minRTT for the entire connection.
    36  // May return Zero if no valid updates have occurred.
    37  func (r *RTTStats) MinRTT() time.Duration { return r.minRTT }
    38  
    39  // LatestRTT returns the most recent rtt measurement.
    40  // May return Zero if no valid updates have occurred.
    41  func (r *RTTStats) LatestRTT() time.Duration { return r.latestRTT }
    42  
    43  // SmoothedRTT returns the smoothed RTT for the connection.
    44  // May return Zero if no valid updates have occurred.
    45  func (r *RTTStats) SmoothedRTT() time.Duration { return r.smoothedRTT }
    46  
    47  // MeanDeviation gets the mean deviation
    48  func (r *RTTStats) MeanDeviation() time.Duration { return r.meanDeviation }
    49  
    50  // MaxAckDelay gets the max_ack_delay advertised by the peer
    51  func (r *RTTStats) MaxAckDelay() time.Duration { return r.maxAckDelay }
    52  
    53  // PTO gets the probe timeout duration.
    54  func (r *RTTStats) PTO(includeMaxAckDelay bool) time.Duration {
    55  	if r.SmoothedRTT() == 0 {
    56  		return 2 * defaultInitialRTT
    57  	}
    58  	pto := r.SmoothedRTT() + max(4*r.MeanDeviation(), protocol.TimerGranularity)
    59  	if includeMaxAckDelay {
    60  		pto += r.MaxAckDelay()
    61  	}
    62  	return pto
    63  }
    64  
    65  // UpdateRTT updates the RTT based on a new sample.
    66  func (r *RTTStats) UpdateRTT(sendDelta, ackDelay time.Duration, now time.Time) {
    67  	if sendDelta == InfDuration || sendDelta <= 0 {
    68  		return
    69  	}
    70  
    71  	// Update r.minRTT first. r.minRTT does not use an rttSample corrected for
    72  	// ackDelay but the raw observed sendDelta, since poor clock granularity at
    73  	// the client may cause a high ackDelay to result in underestimation of the
    74  	// r.minRTT.
    75  	if r.minRTT == 0 || r.minRTT > sendDelta {
    76  		r.minRTT = sendDelta
    77  	}
    78  
    79  	// Correct for ackDelay if information received from the peer results in a
    80  	// an RTT sample at least as large as minRTT. Otherwise, only use the
    81  	// sendDelta.
    82  	sample := sendDelta
    83  	if sample-r.minRTT >= ackDelay {
    84  		sample -= ackDelay
    85  	}
    86  	r.latestRTT = sample
    87  	// First time call.
    88  	if !r.hasMeasurement {
    89  		r.hasMeasurement = true
    90  		r.smoothedRTT = sample
    91  		r.meanDeviation = sample / 2
    92  	} else {
    93  		r.meanDeviation = time.Duration(oneMinusBeta*float32(r.meanDeviation/time.Microsecond)+rttBeta*float32((r.smoothedRTT-sample).Abs()/time.Microsecond)) * time.Microsecond
    94  		r.smoothedRTT = time.Duration((float32(r.smoothedRTT/time.Microsecond)*oneMinusAlpha)+(float32(sample/time.Microsecond)*rttAlpha)) * time.Microsecond
    95  	}
    96  }
    97  
    98  // SetMaxAckDelay sets the max_ack_delay
    99  func (r *RTTStats) SetMaxAckDelay(mad time.Duration) {
   100  	r.maxAckDelay = mad
   101  }
   102  
   103  // SetInitialRTT sets the initial RTT.
   104  // It is used during the 0-RTT handshake when restoring the RTT stats from the session state.
   105  func (r *RTTStats) SetInitialRTT(t time.Duration) {
   106  	// On the server side, by the time we get to process the session ticket,
   107  	// we might already have obtained an RTT measurement.
   108  	// This can happen if we received the ClientHello in multiple pieces, and one of those pieces was lost.
   109  	// Discard the restored value. A fresh measurement is always better.
   110  	if r.hasMeasurement {
   111  		return
   112  	}
   113  	r.smoothedRTT = t
   114  	r.latestRTT = t
   115  }
   116  
   117  // OnConnectionMigration is called when connection migrates and rtt measurement needs to be reset.
   118  func (r *RTTStats) OnConnectionMigration() {
   119  	r.latestRTT = 0
   120  	r.minRTT = 0
   121  	r.smoothedRTT = 0
   122  	r.meanDeviation = 0
   123  }
   124  
   125  // ExpireSmoothedMetrics causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt
   126  // is larger. The mean deviation is increased to the most recent deviation if
   127  // it's larger.
   128  func (r *RTTStats) ExpireSmoothedMetrics() {
   129  	r.meanDeviation = max(r.meanDeviation, (r.smoothedRTT - r.latestRTT).Abs())
   130  	r.smoothedRTT = max(r.smoothedRTT, r.latestRTT)
   131  }