Call WriteMessage directly in websocketconn.Conn.Write.

In the client←server direction, this hits a fast path that avoids
allocating a messageWriter.
https://github.com/gorilla/websocket/blob/v1.5.0/conn.go#L760

Cuts the number of allocations in half in the client←server direction:

	$ go test -bench=BenchmarkReadWrite -benchmem -benchtime=5s
	BenchmarkReadWrite/c←s_150-4              597511             13358 ns/op          11.23 MB/s       33709 B/op          2 allocs/op
	BenchmarkReadWrite/s←c_150-4              474176             13756 ns/op          10.90 MB/s       34968 B/op          4 allocs/op
	BenchmarkReadWrite/c←s_3000-4             156488             36290 ns/op          82.67 MB/s       68673 B/op          5 allocs/op
	BenchmarkReadWrite/s←c_3000-4             190897             34719 ns/op          86.41 MB/s       69730 B/op          8 allocs/op
This commit is contained in:
David Fifield 2022-09-22 15:43:59 -06:00
parent 8cadcaee70
commit 3df514ae29

View file

@ -64,16 +64,7 @@ func writeLoop(ws *websocket.Conn, r io.Reader) error {
if err != nil { if err != nil {
return err return err
} }
data := buf[:n] err = ws.WriteMessage(websocket.BinaryMessage, buf[:n])
w, err := ws.NextWriter(websocket.BinaryMessage)
if err != nil {
return err
}
n, err = w.Write(data)
if err != nil {
return err
}
err = w.Close()
if err != nil { if err != nil {
return err return err
} }
@ -100,10 +91,10 @@ func New(ws *websocket.Conn) *Conn {
// https://godoc.org/github.com/gorilla/websocket#hdr-Concurrency // https://godoc.org/github.com/gorilla/websocket#hdr-Concurrency
// "Connections support one concurrent reader and one concurrent writer. // "Connections support one concurrent reader and one concurrent writer.
// Applications are responsible for ensuring that no more than one // Applications are responsible for ensuring that no more than one
// goroutine calls the write methods (NextWriter, etc.) concurrently and // goroutine calls the write methods (WriteMessage, etc.) concurrently
// that no more than one goroutine calls the read methods (NextReader, // and that no more than one goroutine calls the read methods
// etc.) concurrently. The Close and WriteControl methods can be called // (NextReader, etc.) concurrently. The Close and WriteControl methods
// concurrently with all other methods." // can be called concurrently with all other methods."
pr1, pw1 := io.Pipe() pr1, pw1 := io.Pipe()
go func() { go func() {
pw1.CloseWithError(closeErrorToEOF(readLoop(pw1, ws))) pw1.CloseWithError(closeErrorToEOF(readLoop(pw1, ws)))