http2: drop benchmarks Already don't exist on go1.27+, but panic on go1.26 due to trying to synctest.Wait outside a bubble. Just delete them. Fixes golang/go#81399 Change-Id: I656f94e56b81a7864202a41499f779176a6a6964 Reviewed-on: https://go-review.googlesource.com/c/net/+/830344 Reviewed-by: Nicholas Husin <husin@google.com> Auto-Submit: Damien Neil <dneil@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Nicholas Husin <nsh@golang.org>
diff --git a/http2/server_test.go b/http2/server_test.go index 5ca49a8..aa63361 100644 --- a/http2/server_test.go +++ b/http2/server_test.go
@@ -2658,162 +2658,6 @@ }) } -func BenchmarkServerGets(b *testing.B) { - DisableGoroutineTracking(b) - b.ReportAllocs() - - const msg = "Hello, world" - st := newServerTesterWithRealConn(b, func(w http.ResponseWriter, r *http.Request) { - io.WriteString(w, msg) - }) - defer st.Close() - st.greet() - - // Give the server quota to reply. (plus it has the 64KB) - if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil { - b.Fatal(err) - } - - for i := 0; i < b.N; i++ { - id := 1 + uint32(i)*2 - st.writeHeaders(HeadersFrameParam{ - StreamID: id, - BlockFragment: st.encodeHeader(), - EndStream: true, - EndHeaders: true, - }) - st.wantFrameType(FrameHeaders) - if df := readFrame[*DataFrame](b, st); !df.StreamEnded() { - b.Fatalf("DATA didn't have END_STREAM; got %v", df) - } - } -} - -func BenchmarkServerPosts(b *testing.B) { - DisableGoroutineTracking(b) - b.ReportAllocs() - - const msg = "Hello, world" - st := newServerTesterWithRealConn(b, func(w http.ResponseWriter, r *http.Request) { - // Consume the (empty) body from th peer before replying, otherwise - // the server will sometimes (depending on scheduling) send the peer a - // a RST_STREAM with the CANCEL error code. - if n, err := io.Copy(io.Discard, r.Body); n != 0 || err != nil { - b.Errorf("Copy error; got %v, %v; want 0, nil", n, err) - } - io.WriteString(w, msg) - }) - defer st.Close() - st.greet() - - // Give the server quota to reply. (plus it has the 64KB) - if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil { - b.Fatal(err) - } - - for i := 0; i < b.N; i++ { - id := 1 + uint32(i)*2 - st.writeHeaders(HeadersFrameParam{ - StreamID: id, - BlockFragment: st.encodeHeader(":method", "POST"), - EndStream: false, - EndHeaders: true, - }) - st.writeData(id, true, nil) - st.wantFrameType(FrameHeaders) - if df := readFrame[*DataFrame](b, st); !df.StreamEnded() { - b.Fatalf("DATA didn't have END_STREAM; got %v", df) - } - } -} - -// Send a stream of messages from server to client in separate data frames. -// Brings up performance issues seen in long streams. -// Created to show problem in go issue #18502 -func BenchmarkServerToClientStreamDefaultOptions(b *testing.B) { - benchmarkServerToClientStream(b) -} - -// Justification for Change-Id: Iad93420ef6c3918f54249d867098f1dadfa324d8 -// Expect to see memory/alloc reduction by opting in to Frame reuse with the Framer. -func BenchmarkServerToClientStreamReuseFrames(b *testing.B) { - benchmarkServerToClientStream(b, optFramerReuseFrames) -} - -func benchmarkServerToClientStream(b *testing.B, newServerOpts ...interface{}) { - DisableGoroutineTracking(b) - b.ReportAllocs() - const msgLen = 1 - // default window size - const windowSize = 1<<16 - 1 - - // next message to send from the server and for the client to expect - nextMsg := func(i int) []byte { - msg := make([]byte, msgLen) - msg[0] = byte(i) - if len(msg) != msgLen { - panic("invalid test setup msg length") - } - return msg - } - - st := newServerTesterWithRealConn(b, func(w http.ResponseWriter, r *http.Request) { - // Consume the (empty) body from th peer before replying, otherwise - // the server will sometimes (depending on scheduling) send the peer a - // a RST_STREAM with the CANCEL error code. - if n, err := io.Copy(io.Discard, r.Body); n != 0 || err != nil { - b.Errorf("Copy error; got %v, %v; want 0, nil", n, err) - } - for i := 0; i < b.N; i += 1 { - w.Write(nextMsg(i)) - w.(http.Flusher).Flush() - } - }, newServerOpts...) - defer st.Close() - st.greet() - - const id = uint32(1) - - st.writeHeaders(HeadersFrameParam{ - StreamID: id, - BlockFragment: st.encodeHeader(":method", "POST"), - EndStream: false, - EndHeaders: true, - }) - - st.writeData(id, true, nil) - st.wantHeaders(wantHeader{ - streamID: 1, - endStream: false, - }) - - var pendingWindowUpdate = uint32(0) - - for i := 0; i < b.N; i += 1 { - expected := nextMsg(i) - st.wantData(wantData{ - streamID: 1, - endStream: false, - data: expected, - }) - // try to send infrequent but large window updates so they don't overwhelm the test - pendingWindowUpdate += uint32(len(expected)) - if pendingWindowUpdate >= windowSize/2 { - if err := st.fr.WriteWindowUpdate(0, pendingWindowUpdate); err != nil { - b.Fatal(err) - } - if err := st.fr.WriteWindowUpdate(id, pendingWindowUpdate); err != nil { - b.Fatal(err) - } - pendingWindowUpdate = 0 - } - } - st.wantData(wantData{ - streamID: 1, - endStream: true, - }) -} - // go-fuzz bug, originally reported at https://github.com/bradfitz/http2/issues/53 // Verify we don't hang. func TestIssue53(t *testing.T) { synctestTest(t, testIssue53) } @@ -3067,70 +2911,6 @@ }) } -func BenchmarkServer_GetRequest(b *testing.B) { - DisableGoroutineTracking(b) - b.ReportAllocs() - const msg = "Hello, world." - st := newServerTesterWithRealConn(b, func(w http.ResponseWriter, r *http.Request) { - n, err := io.Copy(io.Discard, r.Body) - if err != nil || n > 0 { - b.Errorf("Read %d bytes, error %v; want 0 bytes.", n, err) - } - io.WriteString(w, msg) - }) - defer st.Close() - - st.greet() - // Give the server quota to reply. (plus it has the 64KB) - if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil { - b.Fatal(err) - } - hbf := st.encodeHeader(":method", "GET") - for i := 0; i < b.N; i++ { - streamID := uint32(1 + 2*i) - st.writeHeaders(HeadersFrameParam{ - StreamID: streamID, - BlockFragment: hbf, - EndStream: true, - EndHeaders: true, - }) - st.wantFrameType(FrameHeaders) - st.wantFrameType(FrameData) - } -} - -func BenchmarkServer_PostRequest(b *testing.B) { - DisableGoroutineTracking(b) - b.ReportAllocs() - const msg = "Hello, world." - st := newServerTesterWithRealConn(b, func(w http.ResponseWriter, r *http.Request) { - n, err := io.Copy(io.Discard, r.Body) - if err != nil || n > 0 { - b.Errorf("Read %d bytes, error %v; want 0 bytes.", n, err) - } - io.WriteString(w, msg) - }) - defer st.Close() - st.greet() - // Give the server quota to reply. (plus it has the 64KB) - if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil { - b.Fatal(err) - } - hbf := st.encodeHeader(":method", "POST") - for i := 0; i < b.N; i++ { - streamID := uint32(1 + 2*i) - st.writeHeaders(HeadersFrameParam{ - StreamID: streamID, - BlockFragment: hbf, - EndStream: false, - EndHeaders: true, - }) - st.writeData(streamID, true, nil) - st.wantFrameType(FrameHeaders) - st.wantFrameType(FrameData) - } -} - type connStateConn struct { net.Conn cs tls.ConnectionState
diff --git a/http2/transport_test.go b/http2/transport_test.go index 92e1ea5..6c760e4 100644 --- a/http2/transport_test.go +++ b/http2/transport_test.go
@@ -9,9 +9,7 @@ import ( "bufio" "bytes" - "compress/gzip" "context" - crand "crypto/rand" "crypto/tls" "encoding/hex" "errors" @@ -3637,125 +3635,6 @@ tc.writeData(rt.streamID(), true, []byte("payload")) } -func BenchmarkClientRequestHeaders(b *testing.B) { - b.Run(" 0 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 0) }) - b.Run(" 10 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 10, 0) }) - b.Run(" 100 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 100, 0) }) - b.Run("1000 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 1000, 0) }) -} - -func BenchmarkClientResponseHeaders(b *testing.B) { - b.Run(" 0 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 0) }) - b.Run(" 10 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 10) }) - b.Run(" 100 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 100) }) - b.Run("1000 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 1000) }) -} - -func BenchmarkDownloadFrameSize(b *testing.B) { - b.Run(" 16k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 16*1024) }) - b.Run(" 64k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 64*1024) }) - b.Run("128k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 128*1024) }) - b.Run("256k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 256*1024) }) - b.Run("512k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 512*1024) }) -} -func benchLargeDownloadRoundTrip(b *testing.B, frameSize uint32) { - DisableGoroutineTracking(b) - const transferSize = 1024 * 1024 * 1024 // must be multiple of 1M - b.ReportAllocs() - ts := newTestServer(b, - func(w http.ResponseWriter, r *http.Request) { - // test 1GB transfer - w.Header().Set("Content-Length", strconv.Itoa(transferSize)) - w.Header().Set("Content-Transfer-Encoding", "binary") - var data [1024 * 1024]byte - for i := 0; i < transferSize/(1024*1024); i++ { - w.Write(data[:]) - } - }, optQuiet, - ) - - tr := &Transport{TLSClientConfig: tlsConfigInsecure, MaxReadFrameSize: frameSize} - defer tr.CloseIdleConnections() - - req, err := http.NewRequest("GET", ts.URL, nil) - if err != nil { - b.Fatal(err) - } - - b.N = 3 - b.SetBytes(transferSize) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - res, err := tr.RoundTrip(req) - if err != nil { - if res != nil { - res.Body.Close() - } - b.Fatalf("RoundTrip err = %v; want nil", err) - } - data, _ := io.ReadAll(res.Body) - if len(data) != transferSize { - b.Fatalf("Response length invalid") - } - res.Body.Close() - if res.StatusCode != http.StatusOK { - b.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK) - } - } -} - -func BenchmarkClientGzip(b *testing.B) { - DisableGoroutineTracking(b) - b.ReportAllocs() - - const responseSize = 1024 * 1024 - - var buf bytes.Buffer - gz := gzip.NewWriter(&buf) - if _, err := io.CopyN(gz, crand.Reader, responseSize); err != nil { - b.Fatal(err) - } - gz.Close() - - data := buf.Bytes() - ts := newTestServer(b, - func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Encoding", "gzip") - w.Write(data) - }, - optQuiet, - ) - - tr := &Transport{TLSClientConfig: tlsConfigInsecure} - defer tr.CloseIdleConnections() - - req, err := http.NewRequest("GET", ts.URL, nil) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - res, err := tr.RoundTrip(req) - if err != nil { - b.Fatalf("RoundTrip err = %v; want nil", err) - } - if res.StatusCode != http.StatusOK { - b.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK) - } - n, err := io.Copy(io.Discard, res.Body) - res.Body.Close() - if err != nil { - b.Fatalf("RoundTrip err = %v; want nil", err) - } - if n != responseSize { - b.Fatalf("RoundTrip expected %d bytes, got %d", responseSize, n) - } - } -} - // The client closes the connection just after the server got the client's HEADERS // frame, but before the server sends its HEADERS response back. The expected // result is an error on RoundTrip explaining the client closed the connection.
diff --git a/http2/writesched_benchmarks_test.go b/http2/writesched_benchmarks_test.go deleted file mode 100644 index 223e219..0000000 --- a/http2/writesched_benchmarks_test.go +++ /dev/null
@@ -1,195 +0,0 @@ -// 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. - -//go:build !(go1.27 && !http2legacy) - -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) - } -}