github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/transport/tuic/congestion/pacer.go (about) 1 package congestion 2 3 import ( 4 "math" 5 "time" 6 7 "github.com/sagernet/quic-go/congestion" 8 ) 9 10 const ( 11 initialMaxDatagramSize = congestion.ByteCount(1252) 12 MinPacingDelay = time.Millisecond 13 TimerGranularity = time.Millisecond 14 maxBurstSizePackets = 10 15 ) 16 17 // The pacer implements a token bucket pacing algorithm. 18 type pacer struct { 19 budgetAtLastSent congestion.ByteCount 20 maxDatagramSize congestion.ByteCount 21 lastSentTime time.Time 22 getAdjustedBandwidth func() uint64 // in bytes/s 23 } 24 25 func newPacer(getBandwidth func() Bandwidth) *pacer { 26 p := &pacer{ 27 maxDatagramSize: initialMaxDatagramSize, 28 getAdjustedBandwidth: func() uint64 { 29 // Bandwidth is in bits/s. We need the value in bytes/s. 30 bw := uint64(getBandwidth() / BytesPerSecond) 31 // Use a slightly higher value than the actual measured bandwidth. 32 // RTT variations then won't result in under-utilization of the congestion window. 33 // Ultimately, this will result in sending packets as acknowledgments are received rather than when timers fire, 34 // provided the congestion window is fully utilized and acknowledgments arrive at regular intervals. 35 return bw * 5 / 4 36 }, 37 } 38 p.budgetAtLastSent = p.maxBurstSize() 39 return p 40 } 41 42 func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { 43 budget := p.Budget(sendTime) 44 if size > budget { 45 p.budgetAtLastSent = 0 46 } else { 47 p.budgetAtLastSent = budget - size 48 } 49 p.lastSentTime = sendTime 50 } 51 52 func (p *pacer) Budget(now time.Time) congestion.ByteCount { 53 if p.lastSentTime.IsZero() { 54 return p.maxBurstSize() 55 } 56 budget := p.budgetAtLastSent + (congestion.ByteCount(p.getAdjustedBandwidth())*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 57 return Min(p.maxBurstSize(), budget) 58 } 59 60 func (p *pacer) maxBurstSize() congestion.ByteCount { 61 return Max( 62 congestion.ByteCount(uint64((MinPacingDelay+TimerGranularity).Nanoseconds())*p.getAdjustedBandwidth())/1e9, 63 maxBurstSizePackets*p.maxDatagramSize, 64 ) 65 } 66 67 // TimeUntilSend returns when the next packet should be sent. 68 // It returns the zero value of time.Time if a packet can be sent immediately. 69 func (p *pacer) TimeUntilSend() time.Time { 70 if p.budgetAtLastSent >= p.maxDatagramSize { 71 return time.Time{} 72 } 73 return p.lastSentTime.Add(Max( 74 MinPacingDelay, 75 time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond, 76 )) 77 } 78 79 func (p *pacer) SetMaxDatagramSize(s congestion.ByteCount) { 80 p.maxDatagramSize = s 81 }