blob: 636b71327ee0091b0deee58589e55d7839da56d6 [file] [log] [blame]
// 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()
}
}