use a sync.Pool for ACK frames

This commit is contained in:
Marten Seemann
2022-09-06 14:38:00 +03:00
parent c3289188d1
commit 63764c429c
6 changed files with 87 additions and 12 deletions

View File

@@ -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

View File

@@ -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,
@@ -31,3 +35,16 @@ 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))
ranges = append(ranges, f.AckRanges...)
ack := &logging.AckFrame{
AckRanges: ranges,
DelayTime: f.DelayTime,
ECNCE: f.ECNCE,
ECT0: f.ECT0,
ECT1: f.ECT1,
}
return ack
}

View File

@@ -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 {
@@ -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 {

View File

@@ -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)
}

View File

@@ -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)
}
})
})