github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/internal/utils/rtt_stats.go (about) 1 package utils 2 3 import ( 4 "time" 5 6 "github.com/daeuniverse/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 }