Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

congestion: don't use floating point math when calculating pacing times #4148

Merged
merged 1 commit into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions internal/congestion/pacer.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package congestion

import (
"math"
"time"

"github.com/quic-go/quic-go/internal/protocol"
Expand All @@ -26,7 +25,7 @@ func newPacer(getBandwidth func() Bandwidth) *pacer {
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,
// 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
},
Expand All @@ -37,7 +36,7 @@ func newPacer(getBandwidth func() Bandwidth) *pacer {

func (p *pacer) SentPacket(sendTime time.Time, size protocol.ByteCount) {
budget := p.Budget(sendTime)
if size > budget {
if size >= budget {
p.budgetAtLastSent = 0
} else {
p.budgetAtLastSent = budget - size
Expand Down Expand Up @@ -69,10 +68,16 @@ func (p *pacer) TimeUntilSend() time.Time {
if p.budgetAtLastSent >= p.maxDatagramSize {
return time.Time{}
}
return p.lastSentTime.Add(utils.Max(
protocol.MinPacingDelay,
time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.adjustedBandwidth())))*time.Nanosecond,
))
diff := 1e9 * uint64(p.maxDatagramSize-p.budgetAtLastSent)
bw := p.adjustedBandwidth()
// We might need to round up this value.
// Otherwise, we might have a budget (slightly) smaller than the datagram size when the timer expires.
d := diff / bw
// this is effectively a math.Ceil, but using only integer math
if diff%bw > 0 {
d++
}
return p.lastSentTime.Add(utils.Max(protocol.MinPacingDelay, time.Duration(d)*time.Nanosecond))
}

func (p *pacer) SetMaxDatagramSize(s protocol.ByteCount) {
Expand Down
11 changes: 11 additions & 0 deletions internal/congestion/pacer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ var _ = Describe("Pacer", func() {
Expect(p.Budget(t.Add(5 * t2.Sub(t)))).To(BeEquivalentTo(5 * packetSize))
})

It("has enough budget for at least one packet when the timer expires", func() {
t := time.Now()
sendBurst(t)
for bw := uint64(100); bw < uint64(5*initialMaxDatagramSize); bw++ {
bandwidth = bw // reduce the bandwidth to 5 packet per second
t2 := p.TimeUntilSend()
Expect(t2).To(BeTemporally(">", t))
Expect(p.Budget(t2)).To(BeNumerically(">=", initialMaxDatagramSize))
}
})

It("never allows bursts larger than the maximum burst size", func() {
t := time.Now()
sendBurst(t)
Expand Down
Loading