Skip to content

Commit 3fc2087

Browse files
authored
Merge pull request #85 from nhooyr/poolbuf
Pool buffers in wspb and wsjson
2 parents 631c152 + e9d8945 commit 3fc2087

File tree

4 files changed

+109
-6
lines changed

4 files changed

+109
-6
lines changed

Diff for: internal/bpool/bpool.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package bpool
2+
3+
import (
4+
"bytes"
5+
"sync"
6+
)
7+
8+
var bpool sync.Pool
9+
10+
// Get returns a buffer from the pool or creates a new one if
11+
// the pool is empty.
12+
func Get() *bytes.Buffer {
13+
b, ok := bpool.Get().(*bytes.Buffer)
14+
if !ok {
15+
b = &bytes.Buffer{}
16+
}
17+
return b
18+
}
19+
20+
// Put returns a buffer into the pool.
21+
func Put(b *bytes.Buffer) {
22+
b.Reset()
23+
bpool.Put(b)
24+
}

Diff for: internal/bpool/bpool_test.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package bpool
2+
3+
import (
4+
"strconv"
5+
"sync"
6+
"testing"
7+
)
8+
9+
func BenchmarkSyncPool(b *testing.B) {
10+
sizes := []int{
11+
2,
12+
16,
13+
32,
14+
64,
15+
128,
16+
256,
17+
512,
18+
4096,
19+
16384,
20+
}
21+
for _, size := range sizes {
22+
b.Run(strconv.Itoa(size), func(b *testing.B) {
23+
b.Run("allocate", func(b *testing.B) {
24+
b.ReportAllocs()
25+
for i := 0; i < b.N; i++ {
26+
buf := make([]byte, size)
27+
_ = buf
28+
}
29+
})
30+
b.Run("pool", func(b *testing.B) {
31+
b.ReportAllocs()
32+
33+
p := sync.Pool{}
34+
35+
b.ResetTimer()
36+
for i := 0; i < b.N; i++ {
37+
buf := p.Get()
38+
if buf == nil {
39+
buf = make([]byte, size)
40+
}
41+
42+
p.Put(buf)
43+
}
44+
})
45+
})
46+
}
47+
}

Diff for: wsjson/wsjson.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"golang.org/x/xerrors"
99

1010
"nhooyr.io/websocket"
11+
"nhooyr.io/websocket/internal/bpool"
1112
)
1213

1314
// Read reads a json message from c into v.
@@ -22,7 +23,7 @@ func Read(ctx context.Context, c *websocket.Conn, v interface{}) error {
2223
}
2324

2425
func read(ctx context.Context, c *websocket.Conn, v interface{}) error {
25-
typ, b, err := c.Read(ctx)
26+
typ, r, err := c.Reader(ctx)
2627
if err != nil {
2728
return err
2829
}
@@ -32,7 +33,17 @@ func read(ctx context.Context, c *websocket.Conn, v interface{}) error {
3233
return xerrors.Errorf("unexpected frame type for json (expected %v): %v", websocket.MessageText, typ)
3334
}
3435

35-
err = json.Unmarshal(b, v)
36+
b := bpool.Get()
37+
defer func() {
38+
bpool.Put(b)
39+
}()
40+
41+
_, err = b.ReadFrom(r)
42+
if err != nil {
43+
return err
44+
}
45+
46+
err = json.Unmarshal(b.Bytes(), v)
3647
if err != nil {
3748
return xerrors.Errorf("failed to unmarshal json: %w", err)
3849
}

Diff for: wspb/wspb.go

+25-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
package wspb
33

44
import (
5+
"bytes"
56
"context"
7+
"sync"
68

79
"github.com/golang/protobuf/proto"
810
"golang.org/x/xerrors"
911

1012
"nhooyr.io/websocket"
13+
"nhooyr.io/websocket/internal/bpool"
1114
)
1215

1316
// Read reads a protobuf message from c into v.
@@ -21,7 +24,7 @@ func Read(ctx context.Context, c *websocket.Conn, v proto.Message) error {
2124
}
2225

2326
func read(ctx context.Context, c *websocket.Conn, v proto.Message) error {
24-
typ, b, err := c.Read(ctx)
27+
typ, r, err := c.Reader(ctx)
2528
if err != nil {
2629
return err
2730
}
@@ -31,7 +34,17 @@ func read(ctx context.Context, c *websocket.Conn, v proto.Message) error {
3134
return xerrors.Errorf("unexpected frame type for protobuf (expected %v): %v", websocket.MessageBinary, typ)
3235
}
3336

34-
err = proto.Unmarshal(b, v)
37+
b := bpool.Get()
38+
defer func() {
39+
bpool.Put(b)
40+
}()
41+
42+
_, err = b.ReadFrom(r)
43+
if err != nil {
44+
return err
45+
}
46+
47+
err = proto.Unmarshal(b.Bytes(), v)
3548
if err != nil {
3649
return xerrors.Errorf("failed to unmarshal protobuf: %w", err)
3750
}
@@ -49,11 +62,19 @@ func Write(ctx context.Context, c *websocket.Conn, v proto.Message) error {
4962
return nil
5063
}
5164

65+
var writeBufPool sync.Pool
66+
5267
func write(ctx context.Context, c *websocket.Conn, v proto.Message) error {
53-
b, err := proto.Marshal(v)
68+
b := bpool.Get()
69+
pb := proto.NewBuffer(b.Bytes())
70+
defer func() {
71+
bpool.Put(bytes.NewBuffer(pb.Bytes()))
72+
}()
73+
74+
err := pb.Marshal(v)
5475
if err != nil {
5576
return xerrors.Errorf("failed to marshal protobuf: %w", err)
5677
}
5778

58-
return c.Write(ctx, websocket.MessageBinary, b)
79+
return c.Write(ctx, websocket.MessageBinary, pb.Bytes())
5980
}

0 commit comments

Comments
 (0)