// Copyright 2023 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.21 package quic import ( "context" "fmt" "io" "math" "sync" "testing" ) // BenchmarkThroughput is based on the crypto/tls benchmark of the same name. func BenchmarkThroughput(b *testing.B) { for size := 1; size <= 64; size <<= 1 { name := fmt.Sprintf("%dMiB", size) b.Run(name, func(b *testing.B) { throughput(b, int64(size<<20)) }) } } func throughput(b *testing.B, totalBytes int64) { // Same buffer size as crypto/tls's BenchmarkThroughput, for consistency. const bufsize = 32 << 10 cli, srv := newLocalConnPair(b, &Config{}, &Config{}) go func() { buf := make([]byte, bufsize) for i := 0; i < b.N; i++ { sconn, err := srv.AcceptStream(context.Background()) if err != nil { panic(fmt.Errorf("AcceptStream: %v", err)) } if _, err := io.CopyBuffer(sconn, sconn, buf); err != nil { panic(fmt.Errorf("CopyBuffer: %v", err)) } sconn.Close() } }() b.SetBytes(totalBytes) buf := make([]byte, bufsize) chunks := int(math.Ceil(float64(totalBytes) / float64(len(buf)))) for i := 0; i < b.N; i++ { cconn, err := cli.NewStream(context.Background()) if err != nil { b.Fatalf("NewStream: %v", err) } closec := make(chan struct{}) go func() { defer close(closec) buf := make([]byte, bufsize) if _, err := io.CopyBuffer(io.Discard, cconn, buf); err != nil { panic(fmt.Errorf("Discard: %v", err)) } }() for j := 0; j < chunks; j++ { _, err := cconn.Write(buf) if err != nil { b.Fatalf("Write: %v", err) } } cconn.CloseWrite() <-closec cconn.Close() } } func BenchmarkReadByte(b *testing.B) { cli, srv := newLocalConnPair(b, &Config{}, &Config{}) var wg sync.WaitGroup defer wg.Wait() wg.Add(1) go func() { defer wg.Done() buf := make([]byte, 1<<20) sconn, err := srv.AcceptStream(context.Background()) if err != nil { panic(fmt.Errorf("AcceptStream: %v", err)) } for { if _, err := sconn.Write(buf); err != nil { break } sconn.Flush() } }() b.SetBytes(1) cconn, err := cli.NewStream(context.Background()) if err != nil { b.Fatalf("NewStream: %v", err) } cconn.Flush() for i := 0; i < b.N; i++ { _, err := cconn.ReadByte() if err != nil { b.Fatalf("ReadByte: %v", err) } } cconn.Close() } func BenchmarkWriteByte(b *testing.B) { cli, srv := newLocalConnPair(b, &Config{}, &Config{}) var wg sync.WaitGroup defer wg.Wait() wg.Add(1) go func() { defer wg.Done() sconn, err := srv.AcceptStream(context.Background()) if err != nil { panic(fmt.Errorf("AcceptStream: %v", err)) } n, err := io.Copy(io.Discard, sconn) if n != int64(b.N) || err != nil { b.Errorf("server io.Copy() = %v, %v; want %v, nil", n, err, b.N) } }() b.SetBytes(1) cconn, err := cli.NewStream(context.Background()) if err != nil { b.Fatalf("NewStream: %v", err) } cconn.Flush() for i := 0; i < b.N; i++ { if err := cconn.WriteByte(0); err != nil { b.Fatalf("WriteByte: %v", err) } } cconn.Close() } func BenchmarkStreamCreation(b *testing.B) { cli, srv := newLocalConnPair(b, &Config{}, &Config{}) go func() { for i := 0; i < b.N; i++ { sconn, err := srv.AcceptStream(context.Background()) if err != nil { panic(fmt.Errorf("AcceptStream: %v", err)) } sconn.Close() } }() buf := make([]byte, 1) for i := 0; i < b.N; i++ { cconn, err := cli.NewStream(context.Background()) if err != nil { b.Fatalf("NewStream: %v", err) } cconn.Write(buf) cconn.Flush() cconn.Read(buf) cconn.Close() } }