mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
use the actual maximum packet size in the pacer
This commit is contained in:
parent
dd8b21f264
commit
91a314258d
4 changed files with 60 additions and 31 deletions
|
@ -21,6 +21,8 @@ const (
|
|||
cubeScale = 40
|
||||
cubeCongestionWindowScale = 410
|
||||
cubeFactor protocol.ByteCount = 1 << cubeScale / cubeCongestionWindowScale / maxDatagramSize
|
||||
// TODO: when re-enabling cubic, make sure to use the actual packet size here
|
||||
maxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4)
|
||||
)
|
||||
|
||||
const defaultNumConnections = 1
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
const (
|
||||
// maxDatagramSize is the default maximum packet size used in the Linux TCP implementation.
|
||||
// Used in QUIC for congestion window computations in bytes.
|
||||
maxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4)
|
||||
initialMaxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4)
|
||||
maxBurstPackets = 3
|
||||
renoBeta = 0.7 // Reno backoff factor.
|
||||
|
@ -291,4 +290,5 @@ func (c *cubicSender) SetMaxDatagramSize(s protocol.ByteCount) {
|
|||
if cwndIsMinCwnd {
|
||||
c.congestionWindow = c.minCongestionWindow()
|
||||
}
|
||||
c.pacer.SetMaxDatagramSize(s)
|
||||
}
|
||||
|
|
|
@ -8,25 +8,29 @@ import (
|
|||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
)
|
||||
|
||||
const maxBurstSize = 10 * maxDatagramSize
|
||||
const maxBurstSizePackets = 10
|
||||
|
||||
// The pacer implements a token bucket pacing algorithm.
|
||||
type pacer struct {
|
||||
budgetAtLastSent protocol.ByteCount
|
||||
maxDatagramSize protocol.ByteCount
|
||||
lastSentTime time.Time
|
||||
getAdjustedBandwidth func() uint64 // in bytes/s
|
||||
}
|
||||
|
||||
func newPacer(getBandwidth func() Bandwidth) *pacer {
|
||||
p := &pacer{getAdjustedBandwidth: func() uint64 {
|
||||
// Bandwidth is in bits/s. We need the value in bytes/s.
|
||||
bw := uint64(getBandwidth() / BytesPerSecond)
|
||||
// Use a slightly higher value than the actual measured bandwidth.
|
||||
// RTT variations then won't result in under-utilization of the congestion window.
|
||||
// Ultimately, this will result in sending packets as acknowledgments are received rather than when timers fire,
|
||||
// provided the congestion window is fully utilized and acknowledgments arrive at regular intervals.
|
||||
return bw * 5 / 4
|
||||
}}
|
||||
p := &pacer{
|
||||
maxDatagramSize: initialMaxDatagramSize,
|
||||
getAdjustedBandwidth: func() uint64 {
|
||||
// Bandwidth is in bits/s. We need the value in bytes/s.
|
||||
bw := uint64(getBandwidth() / BytesPerSecond)
|
||||
// Use a slightly higher value than the actual measured bandwidth.
|
||||
// RTT variations then won't result in under-utilization of the congestion window.
|
||||
// Ultimately, this will result in sending packets as acknowledgments are received rather than when timers fire,
|
||||
// provided the congestion window is fully utilized and acknowledgments arrive at regular intervals.
|
||||
return bw * 5 / 4
|
||||
},
|
||||
}
|
||||
p.budgetAtLastSent = p.maxBurstSize()
|
||||
return p
|
||||
}
|
||||
|
@ -52,18 +56,22 @@ func (p *pacer) Budget(now time.Time) protocol.ByteCount {
|
|||
func (p *pacer) maxBurstSize() protocol.ByteCount {
|
||||
return utils.MaxByteCount(
|
||||
protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.getAdjustedBandwidth())/1e9,
|
||||
maxBurstSize,
|
||||
maxBurstSizePackets*p.maxDatagramSize,
|
||||
)
|
||||
}
|
||||
|
||||
// TimeUntilSend returns when the next packet should be sent.
|
||||
// It returns the zero value of time.Time if a packet can be sent immediately.
|
||||
func (p *pacer) TimeUntilSend() time.Time {
|
||||
if p.budgetAtLastSent >= maxDatagramSize {
|
||||
if p.budgetAtLastSent >= p.maxDatagramSize {
|
||||
return time.Time{}
|
||||
}
|
||||
return p.lastSentTime.Add(utils.MaxDuration(
|
||||
protocol.MinPacingDelay,
|
||||
time.Duration(math.Ceil(float64(maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond,
|
||||
time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond,
|
||||
))
|
||||
}
|
||||
|
||||
func (p *pacer) SetMaxDatagramSize(s protocol.ByteCount) {
|
||||
p.maxDatagramSize = s
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ var _ = Describe("Pacer", func() {
|
|||
var bandwidth uint64 // in bytes/s
|
||||
|
||||
BeforeEach(func() {
|
||||
bandwidth = uint64(packetsPerSecond * maxDatagramSize) // 50 full-size packets per second
|
||||
bandwidth = uint64(packetsPerSecond * initialMaxDatagramSize) // 50 full-size packets per second
|
||||
// The pacer will multiply the bandwidth with 1.25 to achieve a slightly higher pacing speed.
|
||||
// For the tests, cancel out this factor, so we can do the math using the exact bandwidth.
|
||||
p = newPacer(func() Bandwidth { return Bandwidth(bandwidth) * BytesPerSecond * 4 / 5 })
|
||||
|
@ -25,14 +25,14 @@ var _ = Describe("Pacer", func() {
|
|||
It("allows a burst at the beginning", func() {
|
||||
t := time.Now()
|
||||
Expect(p.TimeUntilSend()).To(BeZero())
|
||||
Expect(p.Budget(t)).To(BeEquivalentTo(maxBurstSize))
|
||||
Expect(p.Budget(t)).To(BeEquivalentTo(maxBurstSizePackets * initialMaxDatagramSize))
|
||||
})
|
||||
|
||||
It("allows a big burst for high pacing rates", func() {
|
||||
t := time.Now()
|
||||
bandwidth = uint64(10000 * packetsPerSecond * maxDatagramSize)
|
||||
bandwidth = uint64(10000 * packetsPerSecond * initialMaxDatagramSize)
|
||||
Expect(p.TimeUntilSend()).To(BeZero())
|
||||
Expect(p.Budget(t)).To(BeNumerically(">", maxBurstSize))
|
||||
Expect(p.Budget(t)).To(BeNumerically(">", maxBurstSizePackets*initialMaxDatagramSize))
|
||||
})
|
||||
|
||||
It("reduces the budget when sending packets", func() {
|
||||
|
@ -41,8 +41,8 @@ var _ = Describe("Pacer", func() {
|
|||
for budget > 0 {
|
||||
Expect(p.TimeUntilSend()).To(BeZero())
|
||||
Expect(p.Budget(t)).To(Equal(budget))
|
||||
p.SentPacket(t, maxDatagramSize)
|
||||
budget -= maxDatagramSize
|
||||
p.SentPacket(t, initialMaxDatagramSize)
|
||||
budget -= initialMaxDatagramSize
|
||||
}
|
||||
Expect(p.Budget(t)).To(BeZero())
|
||||
Expect(p.TimeUntilSend()).ToNot(BeZero())
|
||||
|
@ -50,7 +50,7 @@ var _ = Describe("Pacer", func() {
|
|||
|
||||
sendBurst := func(t time.Time) {
|
||||
for p.Budget(t) > 0 {
|
||||
p.SentPacket(t, maxDatagramSize)
|
||||
p.SentPacket(t, initialMaxDatagramSize)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,8 +61,8 @@ var _ = Describe("Pacer", func() {
|
|||
for i := 0; i < 100; i++ {
|
||||
t2 := p.TimeUntilSend()
|
||||
Expect(t2.Sub(t)).To(BeNumerically("~", time.Second/packetsPerSecond, time.Nanosecond))
|
||||
Expect(p.Budget(t2)).To(BeEquivalentTo(maxDatagramSize))
|
||||
p.SentPacket(t2, maxDatagramSize)
|
||||
Expect(p.Budget(t2)).To(BeEquivalentTo(initialMaxDatagramSize))
|
||||
p.SentPacket(t2, initialMaxDatagramSize)
|
||||
t = t2
|
||||
}
|
||||
})
|
||||
|
@ -73,10 +73,10 @@ var _ = Describe("Pacer", func() {
|
|||
t2 := p.TimeUntilSend()
|
||||
Expect(t2.Sub(t)).To(BeNumerically("~", time.Second/packetsPerSecond, time.Nanosecond))
|
||||
// send a half-full packet
|
||||
Expect(p.Budget(t2)).To(BeEquivalentTo(maxDatagramSize))
|
||||
size := maxDatagramSize / 2
|
||||
Expect(p.Budget(t2)).To(BeEquivalentTo(initialMaxDatagramSize))
|
||||
size := initialMaxDatagramSize / 2
|
||||
p.SentPacket(t2, size)
|
||||
Expect(p.Budget(t2)).To(Equal(maxDatagramSize - size))
|
||||
Expect(p.Budget(t2)).To(Equal(initialMaxDatagramSize - size))
|
||||
Expect(p.TimeUntilSend()).To(BeTemporally("~", t2.Add(time.Second/packetsPerSecond/2), time.Nanosecond))
|
||||
})
|
||||
|
||||
|
@ -86,27 +86,46 @@ var _ = Describe("Pacer", func() {
|
|||
t2 := p.TimeUntilSend()
|
||||
Expect(t2).To(BeTemporally(">", t))
|
||||
// wait for 5 times the duration
|
||||
Expect(p.Budget(t.Add(5 * t2.Sub(t)))).To(BeEquivalentTo(5 * maxDatagramSize))
|
||||
Expect(p.Budget(t.Add(5 * t2.Sub(t)))).To(BeEquivalentTo(5 * initialMaxDatagramSize))
|
||||
})
|
||||
|
||||
It("accumulates budget, if no packets are sent, for larger packet sizes", func() {
|
||||
t := time.Now()
|
||||
sendBurst(t)
|
||||
const packetSize = initialMaxDatagramSize + 200
|
||||
p.SetMaxDatagramSize(packetSize)
|
||||
t2 := p.TimeUntilSend()
|
||||
Expect(t2).To(BeTemporally(">", t))
|
||||
// wait for 5 times the duration
|
||||
Expect(p.Budget(t.Add(5 * t2.Sub(t)))).To(BeEquivalentTo(5 * packetSize))
|
||||
})
|
||||
|
||||
It("never allows bursts larger than the maximum burst size", func() {
|
||||
t := time.Now()
|
||||
sendBurst(t)
|
||||
Expect(p.Budget(t.Add(time.Hour))).To(BeEquivalentTo(maxBurstSize))
|
||||
Expect(p.Budget(t.Add(time.Hour))).To(BeEquivalentTo(maxBurstSizePackets * initialMaxDatagramSize))
|
||||
})
|
||||
|
||||
It("never allows bursts larger than the maximum burst size, for larger packets", func() {
|
||||
t := time.Now()
|
||||
const packetSize = initialMaxDatagramSize + 200
|
||||
p.SetMaxDatagramSize(packetSize)
|
||||
sendBurst(t)
|
||||
Expect(p.Budget(t.Add(time.Hour))).To(BeEquivalentTo(maxBurstSizePackets * packetSize))
|
||||
})
|
||||
|
||||
It("changes the bandwidth", func() {
|
||||
t := time.Now()
|
||||
sendBurst(t)
|
||||
bandwidth = uint64(5 * maxDatagramSize) // reduce the bandwidth to 5 packet per second
|
||||
bandwidth = uint64(5 * initialMaxDatagramSize) // reduce the bandwidth to 5 packet per second
|
||||
Expect(p.TimeUntilSend()).To(Equal(t.Add(time.Second / 5)))
|
||||
})
|
||||
|
||||
It("doesn't pace faster than the minimum pacing duration", func() {
|
||||
t := time.Now()
|
||||
sendBurst(t)
|
||||
bandwidth = uint64(1e6 * maxDatagramSize)
|
||||
bandwidth = uint64(1e6 * initialMaxDatagramSize)
|
||||
Expect(p.TimeUntilSend()).To(Equal(t.Add(protocol.MinPacingDelay)))
|
||||
Expect(p.Budget(t.Add(protocol.MinPacingDelay))).To(Equal(protocol.ByteCount(protocol.MinPacingDelay) * maxDatagramSize * 1e6 / 1e9))
|
||||
Expect(p.Budget(t.Add(protocol.MinPacingDelay))).To(Equal(protocol.ByteCount(protocol.MinPacingDelay) * initialMaxDatagramSize * 1e6 / 1e9))
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue