github.com/metacubex/mihomo@v1.18.5/transport/hysteria/congestion/brutal.go (about)

     1  package congestion
     2  
     3  import (
     4  	"github.com/metacubex/quic-go/congestion"
     5  	"time"
     6  )
     7  
     8  const (
     9  	initMaxDatagramSize = 1252
    10  
    11  	pktInfoSlotCount = 5 // slot index is based on seconds, so this is basically how many seconds we sample
    12  	minSampleCount   = 50
    13  	minAckRate       = 0.8
    14  )
    15  
    16  var _ congestion.CongestionControlEx = &BrutalSender{}
    17  
    18  type BrutalSender struct {
    19  	rttStats        congestion.RTTStatsProvider
    20  	bps             congestion.ByteCount
    21  	maxDatagramSize congestion.ByteCount
    22  	pacer           *pacer
    23  
    24  	pktInfoSlots [pktInfoSlotCount]pktInfo
    25  	ackRate      float64
    26  }
    27  
    28  type pktInfo struct {
    29  	Timestamp int64
    30  	AckCount  uint64
    31  	LossCount uint64
    32  }
    33  
    34  func NewBrutalSender(bps congestion.ByteCount) *BrutalSender {
    35  	bs := &BrutalSender{
    36  		bps:             bps,
    37  		maxDatagramSize: initMaxDatagramSize,
    38  		ackRate:         1,
    39  	}
    40  	bs.pacer = newPacer(func() congestion.ByteCount {
    41  		return congestion.ByteCount(float64(bs.bps) / bs.ackRate)
    42  	})
    43  	return bs
    44  }
    45  
    46  func (b *BrutalSender) SetRTTStatsProvider(rttStats congestion.RTTStatsProvider) {
    47  	b.rttStats = rttStats
    48  }
    49  
    50  func (b *BrutalSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time {
    51  	return b.pacer.TimeUntilSend()
    52  }
    53  
    54  func (b *BrutalSender) HasPacingBudget(now time.Time) bool {
    55  	return b.pacer.Budget(now) >= b.maxDatagramSize
    56  }
    57  
    58  func (b *BrutalSender) CanSend(bytesInFlight congestion.ByteCount) bool {
    59  	return bytesInFlight < b.GetCongestionWindow()
    60  }
    61  
    62  func (b *BrutalSender) GetCongestionWindow() congestion.ByteCount {
    63  	rtt := maxDuration(b.rttStats.LatestRTT(), b.rttStats.SmoothedRTT())
    64  	if rtt <= 0 {
    65  		return 10240
    66  	}
    67  	return congestion.ByteCount(float64(b.bps) * rtt.Seconds() * 1.5 / b.ackRate)
    68  }
    69  
    70  func (b *BrutalSender) OnPacketSent(sentTime time.Time, bytesInFlight congestion.ByteCount,
    71  	packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) {
    72  	b.pacer.SentPacket(sentTime, bytes)
    73  }
    74  
    75  func (b *BrutalSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount,
    76  	priorInFlight congestion.ByteCount, eventTime time.Time) {
    77  	// Stub
    78  }
    79  
    80  func (b *BrutalSender) OnCongestionEvent(number congestion.PacketNumber, lostBytes congestion.ByteCount,
    81  	priorInFlight congestion.ByteCount) {
    82  	// Stub
    83  }
    84  
    85  func (b *BrutalSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) {
    86  	currentTimestamp := eventTime.Unix()
    87  	slot := currentTimestamp % pktInfoSlotCount
    88  	if b.pktInfoSlots[slot].Timestamp == currentTimestamp {
    89  		b.pktInfoSlots[slot].LossCount += uint64(len(lostPackets))
    90  		b.pktInfoSlots[slot].AckCount += uint64(len(ackedPackets))
    91  	} else {
    92  		// uninitialized slot or too old, reset
    93  		b.pktInfoSlots[slot].Timestamp = currentTimestamp
    94  		b.pktInfoSlots[slot].AckCount = uint64(len(ackedPackets))
    95  		b.pktInfoSlots[slot].LossCount = uint64(len(lostPackets))
    96  	}
    97  	b.updateAckRate(currentTimestamp)
    98  }
    99  
   100  func (b *BrutalSender) SetMaxDatagramSize(size congestion.ByteCount) {
   101  	b.maxDatagramSize = size
   102  	b.pacer.SetMaxDatagramSize(size)
   103  }
   104  
   105  func (b *BrutalSender) updateAckRate(currentTimestamp int64) {
   106  	minTimestamp := currentTimestamp - pktInfoSlotCount
   107  	var ackCount, lossCount uint64
   108  	for _, info := range b.pktInfoSlots {
   109  		if info.Timestamp < minTimestamp {
   110  			continue
   111  		}
   112  		ackCount += info.AckCount
   113  		lossCount += info.LossCount
   114  	}
   115  	if ackCount+lossCount < minSampleCount {
   116  		b.ackRate = 1
   117  		return
   118  	}
   119  	rate := float64(ackCount) / float64(ackCount+lossCount)
   120  	if rate < minAckRate {
   121  		b.ackRate = minAckRate
   122  		return
   123  	}
   124  	b.ackRate = rate
   125  }
   126  
   127  func (b *BrutalSender) InSlowStart() bool {
   128  	return false
   129  }
   130  
   131  func (b *BrutalSender) InRecovery() bool {
   132  	return false
   133  }
   134  
   135  func (b *BrutalSender) MaybeExitSlowStart() {}
   136  
   137  func (b *BrutalSender) OnRetransmissionTimeout(packetsRetransmitted bool) {}
   138  
   139  func maxDuration(a, b time.Duration) time.Duration {
   140  	if a > b {
   141  		return a
   142  	}
   143  	return b
   144  }