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