mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-07 06:07:36 +03:00
continue hybrid slow start implementation
This commit is contained in:
parent
4806807caa
commit
3feb288817
3 changed files with 107 additions and 3 deletions
|
@ -1,6 +1,23 @@
|
|||
package congestion
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
// Note(pwestin): the magic clamping numbers come from the original code in
|
||||
// tcp_cubic.c.
|
||||
const hybridStartLowWindow = uint64(16)
|
||||
|
||||
// Number of delay samples for detecting the increase of delay.
|
||||
const hybridStartMinSamples = uint32(8)
|
||||
|
||||
// Exit slow start if the min rtt has increased by more than 1/8th.
|
||||
const hybridStartDelayFactorExp = 3 // 2^3 = 8
|
||||
// The original paper specifies 2 and 8ms, but those have changed over time.
|
||||
const hybridStartDelayMinThresholdUs = int64(4000)
|
||||
const hybridStartDelayMaxThresholdUs = int64(16000)
|
||||
|
||||
// HybridSlowStart implements the TCP hybrid slow start algorithm
|
||||
type HybridSlowStart struct {
|
||||
|
@ -9,11 +26,12 @@ type HybridSlowStart struct {
|
|||
started bool
|
||||
currentMinRTT time.Duration
|
||||
rttSampleCount uint32
|
||||
hystartFound bool
|
||||
}
|
||||
|
||||
// StartReceiveRound is called for the start of each receive round (burst) in the slow start phase.
|
||||
func (s *HybridSlowStart) StartReceiveRound(last_sent uint64) {
|
||||
s.endPacketNumber = last_sent
|
||||
func (s *HybridSlowStart) StartReceiveRound(lastSent uint64) {
|
||||
s.endPacketNumber = lastSent
|
||||
s.currentMinRTT = 0
|
||||
s.rttSampleCount = 0
|
||||
s.started = true
|
||||
|
@ -23,3 +41,45 @@ func (s *HybridSlowStart) StartReceiveRound(last_sent uint64) {
|
|||
func (s *HybridSlowStart) IsEndOfRound(ack uint64) bool {
|
||||
return s.endPacketNumber < ack
|
||||
}
|
||||
|
||||
// ShouldExitSlowStart should be called on every new ack frame, since a new
|
||||
// RTT measurement can be made then.
|
||||
// rtt: the RTT for this ack packet.
|
||||
// minRTT: is the lowest delay (RTT) we have seen during the session.
|
||||
// congestionWindow: the congestion window in packets.
|
||||
func (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT time.Duration, congestionWindow uint64) bool {
|
||||
if !s.started {
|
||||
// Time to start the hybrid slow start.
|
||||
s.StartReceiveRound(s.lastSentPacketNumber)
|
||||
}
|
||||
if s.hystartFound {
|
||||
return true
|
||||
}
|
||||
// Second detection parameter - delay increase detection.
|
||||
// Compare the minimum delay (s.currentMinRTT) of the current
|
||||
// burst of packets relative to the minimum delay during the session.
|
||||
// Note: we only look at the first few(8) packets in each burst, since we
|
||||
// only want to compare the lowest RTT of the burst relative to previous
|
||||
// bursts.
|
||||
s.rttSampleCount++
|
||||
if s.rttSampleCount <= hybridStartMinSamples {
|
||||
if s.currentMinRTT == 0 || s.currentMinRTT > latestRTT {
|
||||
s.currentMinRTT = latestRTT
|
||||
}
|
||||
}
|
||||
// We only need to check this once per round.
|
||||
if s.rttSampleCount == hybridStartMinSamples {
|
||||
// Divide minRTT by 8 to get a rtt increase threshold for exiting.
|
||||
minRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp)
|
||||
// Ensure the rtt threshold is never less than 2ms or more than 16ms.
|
||||
minRTTincreaseThresholdUs = utils.MinInt64(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs)
|
||||
minRTTincreaseThreshold := time.Duration(utils.MaxInt64(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond
|
||||
|
||||
if s.currentMinRTT > (minRTT + minRTTincreaseThreshold) {
|
||||
s.hystartFound = true
|
||||
}
|
||||
}
|
||||
// Exit from slow start if the cwnd is greater than 16 and
|
||||
// increasing delay is found.
|
||||
return congestionWindow >= hybridStartLowWindow && s.hystartFound
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package congestion_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/congestion"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
@ -44,4 +46,30 @@ var _ = Describe("Hybrid slow start", func() {
|
|||
packet_number++
|
||||
Expect(slowStart.IsEndOfRound(packet_number)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("works with delay", func() {
|
||||
rtt := 60 * time.Millisecond
|
||||
// We expect to detect the increase at +1/8 of the RTT; hence at a typical
|
||||
// RTT of 60ms the detection will happen at 67.5 ms.
|
||||
const kHybridStartMinSamples = 8 // Number of acks required to trigger.
|
||||
|
||||
end_packet_number := uint64(1)
|
||||
end_packet_number++
|
||||
slowStart.StartReceiveRound(end_packet_number)
|
||||
|
||||
// Will not trigger since our lowest RTT in our burst is the same as the long
|
||||
// term RTT provided.
|
||||
for n := 0; n < kHybridStartMinSamples; n++ {
|
||||
Expect(slowStart.ShouldExitSlowStart(rtt+time.Duration(n)*time.Millisecond, rtt, 100)).To(BeFalse())
|
||||
}
|
||||
end_packet_number++
|
||||
slowStart.StartReceiveRound(end_packet_number)
|
||||
for n := 1; n < kHybridStartMinSamples; n++ {
|
||||
Expect(slowStart.ShouldExitSlowStart(rtt+(time.Duration(n)+10)*time.Millisecond, rtt, 100)).To(BeFalse())
|
||||
}
|
||||
// Expect to trigger since all packets in this burst was above the long term
|
||||
// RTT provided.
|
||||
Expect(slowStart.ShouldExitSlowStart(rtt+10*time.Millisecond, rtt, 100)).To(BeTrue())
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -155,6 +155,22 @@ func Min(a, b int) int {
|
|||
return b
|
||||
}
|
||||
|
||||
// MinInt64 returns the minimum of two int64
|
||||
func MinInt64(a, b int64) int64 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// MaxInt64 returns the minimum of two int64
|
||||
func MaxInt64(a, b int64) int64 {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// RandomBit returns a cryptographically secure random bit (encoded as true / false)
|
||||
func RandomBit() (bool, error) {
|
||||
// ToDo: it's probably more efficient to read a bigger slice of random numbers at once and to cache them somewhere
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue