send multiple packets at once, if the pacing delay is very small

This is an optimization to avoid waking of the run loop every couple of
microseconds.
This commit is contained in:
Marten Seemann
2018-01-08 10:03:26 +07:00
parent 5ef89733ae
commit 9ef3a47da5
9 changed files with 214 additions and 88 deletions

View File

@@ -14,8 +14,21 @@ type SentPacketHandler interface {
ReceivedAck(ackFrame *wire.AckFrame, withPacketNumber protocol.PacketNumber, encLevel protocol.EncryptionLevel, recvTime time.Time) error
SetHandshakeComplete()
// SendingAllowed says if a packet can be sent.
// Sending packets might not be possible because:
// * we're congestion limited
// * we're tracking the maximum number of sent packets
SendingAllowed() bool
// TimeUntilSend is the time when the next packet should be sent.
// It is used for pacing packets.
TimeUntilSend() time.Time
// ShouldSendNumPackets returns the number of packets that should be sent immediately.
// It always returns a number greater or equal than 1.
// A number greater than 1 is returned when the pacing delay is smaller than the minimum pacing delay.
// Note that the number of packets is only calculated based on the pacing algorithm.
// Before sending any packet, SendingAllowed() must be called to learn if we can actually send it.
ShouldSendNumPackets() int
GetStopWaitingFrame(force bool) *wire.StopWaitingFrame
GetLowestPacketNotConfirmedAcked() protocol.PacketNumber
ShouldSendRetransmittablePacket() bool

View File

@@ -3,6 +3,7 @@ package ackhandler
import (
"errors"
"fmt"
"math"
"time"
"github.com/lucas-clemente/quic-go/congestion"
@@ -34,7 +35,7 @@ var ErrDuplicateOrOutOfOrderAck = errors.New("SentPacketHandler: Duplicate or ou
type sentPacketHandler struct {
lastSentPacketNumber protocol.PacketNumber
lastPacketSentTime time.Time
nextPacketSendTime time.Time
skippedPackets []protocol.PacketNumber
numNonRetransmittablePackets int // number of non-retransmittable packets since the last retransmittable packet
@@ -125,7 +126,6 @@ func (h *sentPacketHandler) SentPacket(packet *Packet) error {
now := time.Now()
h.lastSentPacketNumber = packet.PacketNumber
h.lastPacketSentTime = now
var largestAcked protocol.PacketNumber
if len(packet.Frames) > 0 {
@@ -155,6 +155,8 @@ func (h *sentPacketHandler) SentPacket(packet *Packet) error {
isRetransmittable,
)
h.nextPacketSendTime = utils.MaxTime(h.nextPacketSendTime, now).Add(h.congestion.TimeUntilSend(h.bytesInFlight))
h.updateLossDetectionAlarm(now)
return nil
}
@@ -389,7 +391,15 @@ func (h *sentPacketHandler) SendingAllowed() bool {
}
func (h *sentPacketHandler) TimeUntilSend() time.Time {
return h.lastPacketSentTime.Add(h.congestion.TimeUntilSend(h.bytesInFlight))
return h.nextPacketSendTime
}
func (h *sentPacketHandler) ShouldSendNumPackets() int {
delay := h.congestion.TimeUntilSend(h.bytesInFlight)
if delay == 0 || delay > protocol.MinPacingDelay {
return 1
}
return int(math.Ceil(float64(protocol.MinPacingDelay) / float64(delay)))
}
func (h *sentPacketHandler) retransmitOldestTwoPackets() {

View File

@@ -680,9 +680,7 @@ var _ = Describe("SentPacketHandler", func() {
})
Context("congestion", func() {
var (
cong *mocks.MockSendAlgorithm
)
var cong *mocks.MockSendAlgorithm
BeforeEach(func() {
cong = mocks.NewMockSendAlgorithm(mockCtrl)
@@ -698,6 +696,7 @@ var _ = Describe("SentPacketHandler", func() {
protocol.ByteCount(42),
true,
)
cong.EXPECT().TimeUntilSend(gomock.Any())
p := &Packet{
PacketNumber: 1,
Length: 42,
@@ -709,6 +708,7 @@ var _ = Describe("SentPacketHandler", func() {
It("should call MaybeExitSlowStart and OnPacketAcked", func() {
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2)
cong.EXPECT().TimeUntilSend(gomock.Any()).Times(2)
cong.EXPECT().MaybeExitSlowStart()
cong.EXPECT().OnPacketAcked(
protocol.PacketNumber(1),
@@ -723,6 +723,7 @@ var _ = Describe("SentPacketHandler", func() {
It("should call MaybeExitSlowStart and OnPacketLost", func() {
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3)
cong.EXPECT().TimeUntilSend(gomock.Any()).Times(3)
cong.EXPECT().OnRetransmissionTimeout(true).Times(2)
cong.EXPECT().OnPacketLost(
protocol.PacketNumber(1),
@@ -765,12 +766,29 @@ var _ = Describe("SentPacketHandler", func() {
})
It("gets the pacing delay", func() {
handler.bytesInFlight = 100
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
cong.EXPECT().TimeUntilSend(protocol.ByteCount(100)).Return(time.Hour)
handler.SentPacket(&Packet{PacketNumber: 1})
handler.bytesInFlight = protocol.ByteCount(100)
cong.EXPECT().TimeUntilSend(handler.bytesInFlight).Return(time.Hour)
Expect(handler.TimeUntilSend()).To(BeTemporally("~", time.Now().Add(time.Hour), time.Second))
})
It("allows sending of one packet, if it should be sent immediately", func() {
cong.EXPECT().TimeUntilSend(gomock.Any()).Return(time.Duration(0))
Expect(handler.ShouldSendNumPackets()).To(Equal(1))
})
It("allows sending of multiple packets, if the pacing delay is smaller than the minimum", func() {
pacingDelay := protocol.MinPacingDelay / 10
cong.EXPECT().TimeUntilSend(gomock.Any()).Return(pacingDelay)
Expect(handler.ShouldSendNumPackets()).To(Equal(10))
})
It("allows sending of multiple packets, if the pacing delay is smaller than the minimum, and not a fraction", func() {
pacingDelay := protocol.MinPacingDelay * 2 / 5
cong.EXPECT().TimeUntilSend(gomock.Any()).Return(pacingDelay)
Expect(handler.ShouldSendNumPackets()).To(Equal(3))
})
})
Context("calculating RTO", func() {