Commit ee9ffb81 authored by David Fifield's avatar David Fifield
Browse files

Benchmark websocket.Conn Upgrade creation.

I had thought to set a buffer size of 2048, half the websocket package
default of 4096. But it turns out when you don't set a buffer size, the
websocket package reuses the HTTP server's read/write buffers, which
empirically already have a size of 2048.

	$ go test -bench=BenchmarkUpgradeBufferSize -benchmem -benchtime=5s
	BenchmarkUpgradeBufferSize/0-4                     25669            234566 ns/op           32604 B/op        113 allocs/op
	BenchmarkUpgradeBufferSize/128-4                   24739            238283 ns/op           24325 B/op        117 allocs/op
	BenchmarkUpgradeBufferSize/1024-4                  25352            238885 ns/op           28087 B/op        116 allocs/op
	BenchmarkUpgradeBufferSize/2048-4                  22660            234890 ns/op           32444 B/op        116 allocs/op
	BenchmarkUpgradeBufferSize/4096-4                  25668            232591 ns/op           41672 B/op        116 allocs/op
	BenchmarkUpgradeBufferSize/8192-4                  24908            240755 ns/op           59103 B/op        116 allocs/op
parent 24d31456
Loading
Loading
Loading
Loading
+67 −0
Original line number Diff line number Diff line
@@ -262,6 +262,73 @@ func TestClose(t *testing.T) {
	}
}

// Benchmark creating a server websocket.Conn (without the websocketconn.Conn
// wrapper) for different read/write buffer sizes.
func BenchmarkUpgradeBufferSize(b *testing.B) {
	// Buffer size of 0 would mean the default of 4096:
	// https://github.com/gorilla/websocket/blob/v1.5.0/conn.go#L37
	// But a size of zero also has the effect of causing reuse of the HTTP
	// server's buffers. So we test 4096 separately from 0.
	// https://github.com/gorilla/websocket/blob/v1.5.0/server.go#L32
	for _, bufSize := range []int{0, 128, 1024, 2048, 4096, 8192} {
		upgrader := websocket.Upgrader{
			CheckOrigin:     func(*http.Request) bool { return true },
			ReadBufferSize:  bufSize,
			WriteBufferSize: bufSize,
		}
		b.Run(fmt.Sprintf("%d", bufSize), func(b *testing.B) {
			// Start up a web server to receive the request.
			ln, err := net.Listen("tcp", "127.0.0.1:0")
			if err != nil {
				b.Fatal(err)
			}
			defer ln.Close()
			wsCh := make(chan *websocket.Conn)
			errCh := make(chan error)
			server := http.Server{
				Handler: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
					ws, err := upgrader.Upgrade(rw, req, nil)
					if err != nil {
						errCh <- err
						return
					}
					wsCh <- ws
				}),
			}
			defer server.Close()
			go func() {
				err := server.Serve(ln)
				if err != nil && err != http.ErrServerClosed {
					errCh <- err
				}
			}()

			// Make a request to the web server.
			dialer := &websocket.Dialer{
				ReadBufferSize:  bufSize,
				WriteBufferSize: bufSize,
			}
			urlStr := (&url.URL{Scheme: "ws", Host: ln.Addr().String()}).String()

			b.ResetTimer()
			for i := 0; i < b.N; i++ {
				ws, _, err := dialer.Dial(urlStr, nil)
				if err != nil {
					b.Fatal(err)
				}
				ws.Close()

				select {
				case <-wsCh:
				case err := <-errCh:
					b.Fatal(err)
				}
			}
			b.StopTimer()
		})
	}
}

// Benchmark read/write in the client←server and server←client directions, with
// messages of different sizes. Run with -benchmem to see memory allocations.
func BenchmarkReadWrite(b *testing.B) {