github.com/MerlinKodo/quic-go@v0.39.2/internal/congestion/hybrid_slow_start.go (about) 1 package congestion 2 3 import ( 4 "time" 5 6 "github.com/MerlinKodo/quic-go/internal/protocol" 7 "github.com/MerlinKodo/quic-go/internal/utils" 8 ) 9 10 // Note(pwestin): the magic clamping numbers come from the original code in 11 // tcp_cubic.c. 12 const hybridStartLowWindow = protocol.ByteCount(16) 13 14 // Number of delay samples for detecting the increase of delay. 15 const hybridStartMinSamples = uint32(8) 16 17 // Exit slow start if the min rtt has increased by more than 1/8th. 18 const hybridStartDelayFactorExp = 3 // 2^3 = 8 19 // The original paper specifies 2 and 8ms, but those have changed over time. 20 const ( 21 hybridStartDelayMinThresholdUs = int64(4000) 22 hybridStartDelayMaxThresholdUs = int64(16000) 23 ) 24 25 // HybridSlowStart implements the TCP hybrid slow start algorithm 26 type HybridSlowStart struct { 27 endPacketNumber protocol.PacketNumber 28 lastSentPacketNumber protocol.PacketNumber 29 started bool 30 currentMinRTT time.Duration 31 rttSampleCount uint32 32 hystartFound bool 33 } 34 35 // StartReceiveRound is called for the start of each receive round (burst) in the slow start phase. 36 func (s *HybridSlowStart) StartReceiveRound(lastSent protocol.PacketNumber) { 37 s.endPacketNumber = lastSent 38 s.currentMinRTT = 0 39 s.rttSampleCount = 0 40 s.started = true 41 } 42 43 // IsEndOfRound returns true if this ack is the last packet number of our current slow start round. 44 func (s *HybridSlowStart) IsEndOfRound(ack protocol.PacketNumber) bool { 45 return s.endPacketNumber < ack 46 } 47 48 // ShouldExitSlowStart should be called on every new ack frame, since a new 49 // RTT measurement can be made then. 50 // rtt: the RTT for this ack packet. 51 // minRTT: is the lowest delay (RTT) we have seen during the session. 52 // congestionWindow: the congestion window in packets. 53 func (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT time.Duration, congestionWindow protocol.ByteCount) bool { 54 if !s.started { 55 // Time to start the hybrid slow start. 56 s.StartReceiveRound(s.lastSentPacketNumber) 57 } 58 if s.hystartFound { 59 return true 60 } 61 // Second detection parameter - delay increase detection. 62 // Compare the minimum delay (s.currentMinRTT) of the current 63 // burst of packets relative to the minimum delay during the session. 64 // Note: we only look at the first few(8) packets in each burst, since we 65 // only want to compare the lowest RTT of the burst relative to previous 66 // bursts. 67 s.rttSampleCount++ 68 if s.rttSampleCount <= hybridStartMinSamples { 69 if s.currentMinRTT == 0 || s.currentMinRTT > latestRTT { 70 s.currentMinRTT = latestRTT 71 } 72 } 73 // We only need to check this once per round. 74 if s.rttSampleCount == hybridStartMinSamples { 75 // Divide minRTT by 8 to get a rtt increase threshold for exiting. 76 minRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp) 77 // Ensure the rtt threshold is never less than 2ms or more than 16ms. 78 minRTTincreaseThresholdUs = utils.Min(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs) 79 minRTTincreaseThreshold := time.Duration(utils.Max(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond 80 81 if s.currentMinRTT > (minRTT + minRTTincreaseThreshold) { 82 s.hystartFound = true 83 } 84 } 85 // Exit from slow start if the cwnd is greater than 16 and 86 // increasing delay is found. 87 return congestionWindow >= hybridStartLowWindow && s.hystartFound 88 } 89 90 // OnPacketSent is called when a packet was sent 91 func (s *HybridSlowStart) OnPacketSent(packetNumber protocol.PacketNumber) { 92 s.lastSentPacketNumber = packetNumber 93 } 94 95 // OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end 96 // the round when the final packet of the burst is received and start it on 97 // the next incoming ack. 98 func (s *HybridSlowStart) OnPacketAcked(ackedPacketNumber protocol.PacketNumber) { 99 if s.IsEndOfRound(ackedPacketNumber) { 100 s.started = false 101 } 102 } 103 104 // Started returns true if started 105 func (s *HybridSlowStart) Started() bool { 106 return s.started 107 } 108 109 // Restart the slow start phase 110 func (s *HybridSlowStart) Restart() { 111 s.started = false 112 s.hystartFound = false 113 }