mirror of
https://github.com/golang/net.git
synced 2026-03-31 18:37:08 +09:00
In RFC 9218, streams are non-incremental by default, meaning that they are processed one-by-one to completion. This behavior is the opposite of our current default of handling streams in a round-robin manner. This might cause a surprising behavior change once we make the RFC 9218 priority scheduler the default write scheduler for most users (we assume that most users will not be sending RFC 9218 priority signals, at least initially). To avoid surprising users with such a behavior change, this CL makes it so that the streams are only made non-incremental once there has been a clear signal that the end-user is aware of RFC 9218. For golang/go#75500 Change-Id: Ibd22cb279c43de0190962904c3809007447a5fe3 Reviewed-on: https://go-review.googlesource.com/c/net/+/729140 Reviewed-by: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Nicholas Husin <husin@google.com>
194 lines
4.9 KiB
Go
194 lines
4.9 KiB
Go
// 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, newPriorityWriteSchedulerRFC9218, PriorityParam{
|
|
incremental: 1,
|
|
})
|
|
}
|
|
|
|
func BenchmarkWriteSchedulerLifetimePriorityRFC9218Incremental(b *testing.B) {
|
|
benchmarkStreamLifetime(b, newPriorityWriteSchedulerRFC9218, PriorityParam{
|
|
incremental: 1,
|
|
})
|
|
}
|
|
|
|
func BenchmarkWriteSchedulerThroughputPriorityRFC9218NonIncremental(b *testing.B) {
|
|
benchmarkThroughput(b, newPriorityWriteSchedulerRFC9218, PriorityParam{
|
|
incremental: 0,
|
|
})
|
|
}
|
|
|
|
func BenchmarkWriteSchedulerLifetimePriorityRFC9218NonIncremental(b *testing.B) {
|
|
benchmarkStreamLifetime(b, newPriorityWriteSchedulerRFC9218, PriorityParam{
|
|
incremental: 0,
|
|
})
|
|
}
|
|
|
|
func BenchmarkWriteQueue(b *testing.B) {
|
|
var qp writeQueuePool
|
|
frameCount := 25
|
|
for b.Loop() {
|
|
q := qp.get()
|
|
for range frameCount {
|
|
q.push(FrameWriteRequest{})
|
|
}
|
|
for !q.empty() {
|
|
// Since we pushed empty frames, consuming 1 byte is enough to
|
|
// consume the entire frame.
|
|
q.consume(1)
|
|
}
|
|
qp.put(q)
|
|
}
|
|
}
|