Skip to content

Commit dda3687

Browse files
committed
quic: add Stream.ReadByte, Stream.WriteByte
Currently unoptimized and slow. Adding along with a benchmark to compare to the fast-path followup. For golang/go#58547 Change-Id: If02b65e6e7cfc770d3f949e5fb9fbb9d8a765a90 Reviewed-on: https://go-review.googlesource.com/c/net/+/564477 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]>
1 parent e94da73 commit dda3687

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

internal/quic/bench_test.go

+71
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"fmt"
1212
"io"
1313
"math"
14+
"sync"
1415
"testing"
1516
)
1617

@@ -72,6 +73,76 @@ func throughput(b *testing.B, totalBytes int64) {
7273
}
7374
}
7475

76+
func BenchmarkReadByte(b *testing.B) {
77+
cli, srv := newLocalConnPair(b, &Config{}, &Config{})
78+
79+
var wg sync.WaitGroup
80+
defer wg.Wait()
81+
82+
wg.Add(1)
83+
go func() {
84+
defer wg.Done()
85+
buf := make([]byte, 1<<20)
86+
sconn, err := srv.AcceptStream(context.Background())
87+
if err != nil {
88+
panic(fmt.Errorf("AcceptStream: %v", err))
89+
}
90+
for {
91+
if _, err := sconn.Write(buf); err != nil {
92+
break
93+
}
94+
sconn.Flush()
95+
}
96+
}()
97+
98+
b.SetBytes(1)
99+
cconn, err := cli.NewStream(context.Background())
100+
if err != nil {
101+
b.Fatalf("NewStream: %v", err)
102+
}
103+
cconn.Flush()
104+
for i := 0; i < b.N; i++ {
105+
_, err := cconn.ReadByte()
106+
if err != nil {
107+
b.Fatalf("ReadByte: %v", err)
108+
}
109+
}
110+
cconn.Close()
111+
}
112+
113+
func BenchmarkWriteByte(b *testing.B) {
114+
cli, srv := newLocalConnPair(b, &Config{}, &Config{})
115+
116+
var wg sync.WaitGroup
117+
defer wg.Wait()
118+
119+
wg.Add(1)
120+
go func() {
121+
defer wg.Done()
122+
sconn, err := srv.AcceptStream(context.Background())
123+
if err != nil {
124+
panic(fmt.Errorf("AcceptStream: %v", err))
125+
}
126+
n, err := io.Copy(io.Discard, sconn)
127+
if n != int64(b.N) || err != nil {
128+
b.Errorf("server io.Copy() = %v, %v; want %v, nil", n, err, b.N)
129+
}
130+
}()
131+
132+
b.SetBytes(1)
133+
cconn, err := cli.NewStream(context.Background())
134+
if err != nil {
135+
b.Fatalf("NewStream: %v", err)
136+
}
137+
cconn.Flush()
138+
for i := 0; i < b.N; i++ {
139+
if err := cconn.WriteByte(0); err != nil {
140+
b.Fatalf("WriteByte: %v", err)
141+
}
142+
}
143+
cconn.Close()
144+
}
145+
75146
func BenchmarkStreamCreation(b *testing.B) {
76147
cli, srv := newLocalConnPair(b, &Config{}, &Config{})
77148

internal/quic/stream.go

+14
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,13 @@ func (s *Stream) Read(b []byte) (n int, err error) {
245245
return len(b), nil
246246
}
247247

248+
// ReadByte reads and returns a single byte from the stream.
249+
func (s *Stream) ReadByte() (byte, error) {
250+
var b [1]byte
251+
_, err := s.Read(b[:])
252+
return b[0], err
253+
}
254+
248255
// shouldUpdateFlowControl determines whether to send a flow control window update.
249256
//
250257
// We want to balance keeping the peer well-supplied with flow control with not sending
@@ -326,6 +333,13 @@ func (s *Stream) Write(b []byte) (n int, err error) {
326333
return n, nil
327334
}
328335

336+
// WriteBytes writes a single byte to the stream.
337+
func (s *Stream) WriteByte(c byte) error {
338+
b := [1]byte{c}
339+
_, err := s.Write(b[:])
340+
return err
341+
}
342+
329343
// Flush flushes data written to the stream.
330344
// It does not wait for the peer to acknowledge receipt of the data.
331345
// Use Close to wait for the peer's acknowledgement.

0 commit comments

Comments
 (0)