Skip to content

Commit

Permalink
use a sync.Pool for ACK frames
Browse files Browse the repository at this point in the history
  • Loading branch information
marten-seemann committed Sep 6, 2022
1 parent c328918 commit 31aeb47
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 12 deletions.
7 changes: 6 additions & 1 deletion connection.go
Expand Up @@ -1334,6 +1334,7 @@ func (s *connection) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel
err = s.handleStreamFrame(frame)
case *wire.AckFrame:
err = s.handleAckFrame(frame, encLevel)
wire.PutAckFrame(frame)
case *wire.ConnectionCloseFrame:
s.handleConnectionCloseFrame(frame)
case *wire.ResetStreamFrame:
Expand Down Expand Up @@ -1952,7 +1953,11 @@ func (s *connection) logPacketContents(p *packetContents) {
for _, f := range p.frames {
frames = append(frames, logutils.ConvertFrame(f.Frame))
}
s.tracer.SentPacket(p.header, p.length, p.ack, frames)
var ack *logging.AckFrame
if p.ack != nil {
ack = logutils.ConvertAckFrame(p.ack)
}
s.tracer.SentPacket(p.header, p.length, ack, frames)
}

// quic-go logging
Expand Down
18 changes: 9 additions & 9 deletions internal/ackhandler/received_packet_tracker.go
Expand Up @@ -171,16 +171,16 @@ func (h *receivedPacketTracker) GetAckFrame(onlyIfQueued bool) *wire.AckFrame {
}
}

ack := &wire.AckFrame{
AckRanges: h.packetHistory.AppendAckRanges(nil),
// Make sure that the DelayTime is always positive.
// This is not guaranteed on systems that don't have a monotonic clock.
DelayTime: utils.Max(0, now.Sub(h.largestObservedReceivedTime)),
ECT0: h.ect0,
ECT1: h.ect1,
ECNCE: h.ecnce,
}
ack := wire.GetAckFrame()
ack.DelayTime = utils.Max(0, now.Sub(h.largestObservedReceivedTime))
ack.ECT0 = h.ect0
ack.ECT1 = h.ect1
ack.ECNCE = h.ecnce
ack.AckRanges = h.packetHistory.AppendAckRanges(ack.AckRanges)

if h.lastAck != nil {
wire.PutAckFrame(h.lastAck)
}
h.lastAck = ack
h.ackAlarm = time.Time{}
h.ackQueued = false
Expand Down
19 changes: 19 additions & 0 deletions internal/logutils/frame.go
Expand Up @@ -11,6 +11,10 @@ import (
// Furthermore, it removes the data slices from CRYPTO and STREAM frames.
func ConvertFrame(frame wire.Frame) logging.Frame {
switch f := frame.(type) {
case *wire.AckFrame:
// We use a pool for ACK frames.
// Implementations of the tracer interface may hold on to frames, so we need to make a copy here.
return ConvertAckFrame(f)
case *wire.CryptoFrame:
return &logging.CryptoFrame{
Offset: f.Offset,
Expand All @@ -31,3 +35,18 @@ func ConvertFrame(frame wire.Frame) logging.Frame {
return logging.Frame(frame)
}
}

func ConvertAckFrame(f *wire.AckFrame) *logging.AckFrame {
ranges := make([]wire.AckRange, 0, len(f.AckRanges))
for _, r := range f.AckRanges {
ranges = append(ranges, r)
}
ack := &logging.AckFrame{
AckRanges: ranges,
DelayTime: f.DelayTime,
ECNCE: f.ECNCE,
ECT0: f.ECT0,
ECT1: f.ECT1,
}
return ack
}
4 changes: 2 additions & 2 deletions internal/wire/ack_frame.go
Expand Up @@ -29,7 +29,7 @@ func parseAckFrame(r *bytes.Reader, ackDelayExponent uint8, _ protocol.VersionNu
}
ecn := typeByte&0x1 > 0

frame := &AckFrame{}
frame := GetAckFrame()

la, err := quicvarint.Read(r)
if err != nil {
Expand Down Expand Up @@ -106,7 +106,7 @@ func parseAckFrame(r *bytes.Reader, ackDelayExponent uint8, _ protocol.VersionNu
return frame, nil
}

// Write writes an ACK frame.
// Append appends an ACK frame.
func (f *AckFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0
if hasECN {
Expand Down
24 changes: 24 additions & 0 deletions internal/wire/ack_frame_pool.go
@@ -0,0 +1,24 @@
package wire

import "sync"

var ackFramePool = sync.Pool{New: func() any {
return &AckFrame{}
}}

func GetAckFrame() *AckFrame {
f := ackFramePool.Get().(*AckFrame)
f.AckRanges = f.AckRanges[:0]
f.ECNCE = 0
f.ECT0 = 0
f.ECT1 = 0
f.DelayTime = 0
return f
}

func PutAckFrame(f *AckFrame) {
if cap(f.AckRanges) > 4 {
return
}
ackFramePool.Put(f)
}
29 changes: 29 additions & 0 deletions internal/wire/ack_frame_pool_test.go
@@ -0,0 +1,29 @@
package wire

import (
"math/rand"
"time"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("ACK Frame (for IETF QUIC)", func() {
It("gets an ACK frame from the pool", func() {
for i := 0; i < 100; i++ {
ack := GetAckFrame()
Expect(ack.AckRanges).To(BeEmpty())
Expect(ack.ECNCE).To(BeZero())
Expect(ack.ECT0).To(BeZero())
Expect(ack.ECT1).To(BeZero())
Expect(ack.DelayTime).To(BeZero())

ack.AckRanges = make([]AckRange, rand.Intn(10))
ack.ECNCE = 1
ack.ECT0 = 2
ack.ECT1 = 3
ack.DelayTime = time.Hour
PutAckFrame(ack)
}
})
})

0 comments on commit 31aeb47

Please sign in to comment.