blob: 274f5041dd7cccbc26f5369cf3c57217732cd0d0 [file] [log] [blame] [edit]
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package http2
import (
"testing"
)
func benchmarkThroughput(b *testing.B, wsFunc func() WriteScheduler, priority PriorityParam) {
const maxFrameSize = 16
const streamCount = 100
ws := wsFunc()
sc := &serverConn{maxFrameSize: maxFrameSize}
streams := make([]*stream, streamCount)
// Possible stream payloads. We vary the payload size of different streams
// to simulate real traffic somewhat.
streamsFrame := [][]byte{
make([]byte, maxFrameSize*5),
make([]byte, maxFrameSize*10),
make([]byte, maxFrameSize*15),
make([]byte, maxFrameSize*20),
make([]byte, maxFrameSize*25),
}
for i := range streams {
streamID := uint32(i) + 1
streams[i] = &stream{
id: streamID,
sc: sc,
}
streams[i].flow.add(1 << 30) // arbitrary large value
ws.OpenStream(streamID, OpenStreamOptions{
priority: priority,
})
}
for b.Loop() {
for i := range streams {
streamID := uint32(i) + 1
ws.Push(FrameWriteRequest{
write: &writeData{
streamID: streamID,
p: streamsFrame[i%len(streamsFrame)],
endStream: false,
},
stream: streams[i],
})
}
for {
wr, ok := ws.Pop()
if !ok {
break
}
if wr.DataSize() != maxFrameSize {
b.Fatalf("wr.Pop() = %v data bytes, want %v", wr.DataSize(), maxFrameSize)
}
}
}
for i := range streams {
streamID := uint32(i) + 1
ws.CloseStream(streamID)
}
}
func benchmarkStreamLifetime(b *testing.B, wsFunc func() WriteScheduler, priority PriorityParam) {
const maxFrameSize = 16
const streamCount = 100
ws := wsFunc()
sc := &serverConn{maxFrameSize: maxFrameSize}
streams := make([]*stream, streamCount)
// Possible stream payloads. We vary the payload size of different streams
// to simulate real traffic somewhat.
streamsFrame := [][]byte{
make([]byte, maxFrameSize*5),
make([]byte, maxFrameSize*10),
make([]byte, maxFrameSize*15),
make([]byte, maxFrameSize*20),
make([]byte, maxFrameSize*25),
}
for i := range streams {
streamID := uint32(i) + 1
streams[i] = &stream{
id: streamID,
sc: sc,
}
streams[i].flow.add(1 << 30) // arbitrary large value
}
for b.Loop() {
for i := range streams {
streamID := uint32(i) + 1
ws.OpenStream(streamID, OpenStreamOptions{
priority: priority,
})
ws.Push(FrameWriteRequest{
write: &writeData{
streamID: streamID,
p: streamsFrame[i%len(streamsFrame)],
endStream: false,
},
stream: streams[i],
})
}
for {
wr, ok := ws.Pop()
if !ok {
break
}
if wr.DataSize() != maxFrameSize {
b.Fatalf("wr.Pop() = %v data bytes, want %v", wr.DataSize(), maxFrameSize)
}
}
for i := range streams {
streamID := uint32(i) + 1
ws.CloseStream(streamID)
}
}
}
func BenchmarkWriteSchedulerThroughputRoundRobin(b *testing.B) {
benchmarkThroughput(b, newRoundRobinWriteScheduler, PriorityParam{})
}
func BenchmarkWriteSchedulerLifetimeRoundRobin(b *testing.B) {
benchmarkStreamLifetime(b, newRoundRobinWriteScheduler, PriorityParam{})
}
func BenchmarkWriteSchedulerThroughputRandom(b *testing.B) {
benchmarkThroughput(b, NewRandomWriteScheduler, PriorityParam{})
}
func BenchmarkWriteSchedulerLifetimeRandom(b *testing.B) {
benchmarkStreamLifetime(b, NewRandomWriteScheduler, PriorityParam{})
}
func BenchmarkWriteSchedulerThroughputPriorityRFC7540(b *testing.B) {
benchmarkThroughput(b, func() WriteScheduler { return NewPriorityWriteScheduler(nil) }, PriorityParam{})
}
func BenchmarkWriteSchedulerLifetimePriorityRFC7540(b *testing.B) {
// RFC7540 priority scheduler does not always succeed in closing the
// stream, causing this benchmark to panic due to opening an already open
// stream.
b.SkipNow()
benchmarkStreamLifetime(b, func() WriteScheduler { return NewPriorityWriteScheduler(nil) }, PriorityParam{})
}
func BenchmarkWriteSchedulerThroughputPriorityRFC9218Incremental(b *testing.B) {
benchmarkThroughput(b, newPriorityWriteSchedulerRFC9128, PriorityParam{
urgency: defaultRFC9218Priority.urgency,
incremental: 1,
})
}
func BenchmarkWriteSchedulerLifetimePriorityRFC9218Incremental(b *testing.B) {
benchmarkStreamLifetime(b, newPriorityWriteSchedulerRFC9128, PriorityParam{
urgency: defaultRFC9218Priority.urgency,
incremental: 1,
})
}
func BenchmarkWriteSchedulerThroughputPriorityRFC9218NonIncremental(b *testing.B) {
benchmarkThroughput(b, newPriorityWriteSchedulerRFC9128, PriorityParam{
urgency: defaultRFC9218Priority.urgency,
incremental: 0,
})
}
func BenchmarkWriteSchedulerLifetimePriorityRFC9218NonIncremental(b *testing.B) {
benchmarkStreamLifetime(b, newPriorityWriteSchedulerRFC9128, PriorityParam{
urgency: defaultRFC9218Priority.urgency,
incremental: 0,
})
}