mirror of
https://github.com/soypat/lneto.git
synced 2026-08-21 15:09:05 +00:00
add benchmarks
This commit is contained in:
@@ -0,0 +1,89 @@
|
|||||||
|
package httphi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// benchConn replays a fixed request and discards the response. It allocates
|
||||||
|
// nothing itself so benchmark alloc counts belong to the package under test.
|
||||||
|
type benchConn struct {
|
||||||
|
request string
|
||||||
|
read int
|
||||||
|
written int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *benchConn) rewind() { c.read, c.written = 0, 0 }
|
||||||
|
|
||||||
|
func (c *benchConn) Read(b []byte) (int, error) {
|
||||||
|
if c.read >= len(c.request) {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
n := copy(b, c.request[c.read:])
|
||||||
|
c.read += n
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *benchConn) Write(b []byte) (int, error) {
|
||||||
|
c.written += len(b)
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *benchConn) Close() error { return nil }
|
||||||
|
|
||||||
|
// benchBody is package level: converting a string literal to []byte inside the
|
||||||
|
// handler would allocate on every request and hide the router's own cost.
|
||||||
|
var benchBody = []byte("hello world")
|
||||||
|
|
||||||
|
func benchExchange(b *testing.B, conn conn) *Exchange {
|
||||||
|
b.Helper()
|
||||||
|
const bufferSize = 1024
|
||||||
|
exch := new(Exchange)
|
||||||
|
exch.Configure(make([]byte, 2*bufferSize), bufferSize, false)
|
||||||
|
if !exch.Acquire(conn) {
|
||||||
|
b.Fatal("fresh exchange failed to acquire connection")
|
||||||
|
}
|
||||||
|
return exch
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkHandle measures a whole exchange: read, parse, mux and respond.
|
||||||
|
func BenchmarkHandle(b *testing.B) {
|
||||||
|
for _, bb := range []struct {
|
||||||
|
name string
|
||||||
|
request string
|
||||||
|
handler HandlerFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "GETWithHeaders",
|
||||||
|
request: "GET / HTTP/1.1\r\nHost: tinygo.org\r\nUser-Agent: bench\r\nAccept: */*\r\nConnection: close\r\n\r\n",
|
||||||
|
handler: func(ex *Exchange) {
|
||||||
|
ex.SetHeader("Content-Type", "text/plain")
|
||||||
|
ex.SetHeaderInt("Content-Length", int64(len(benchBody)), 10)
|
||||||
|
ex.Write(benchBody)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NotFound",
|
||||||
|
request: "GET /nowhere HTTP/1.1\r\nHost: tinygo.org\r\n\r\n",
|
||||||
|
handler: nil, // Unregistered: exercises the 404 path.
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
b.Run(bb.name, func(b *testing.B) {
|
||||||
|
var mux MuxSlice
|
||||||
|
if bb.handler != nil {
|
||||||
|
mux.Handle("GET /", bb.handler)
|
||||||
|
}
|
||||||
|
conn := &benchConn{request: bb.request}
|
||||||
|
exch := benchExchange(b, conn)
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.SetBytes(int64(len(bb.request)))
|
||||||
|
b.ResetTimer()
|
||||||
|
for b.Loop() {
|
||||||
|
conn.rewind()
|
||||||
|
exch.Release()
|
||||||
|
exch.Acquire(conn)
|
||||||
|
Handle(exch, &mux, nopBackoff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,9 +50,10 @@ func TestExchangeWriteHeader(t *testing.T) {
|
|||||||
// Longest status text in status.go: worst case for the status line buffer.
|
// Longest status text in status.go: worst case for the status line buffer.
|
||||||
{code: 511, want: "HTTP/1.1 511 Network Authentication Required\r\n\r\n"},
|
{code: 511, want: "HTTP/1.1 511 Network Authentication Required\r\n\r\n"},
|
||||||
} {
|
} {
|
||||||
exch := newExchange(t, newConn(""), 128, false)
|
conn := newConn("")
|
||||||
|
exch := newExchange(t, conn, 128, false)
|
||||||
exch.WriteHeader(test.code)
|
exch.WriteHeader(test.code)
|
||||||
if got := exch.rw.(*rwconn).ViewWritten(); got != test.want {
|
if got := conn.ViewWritten(); got != test.want {
|
||||||
t.Errorf("code %d: want %q, got %q", test.code, test.want, got)
|
t.Errorf("code %d: want %q, got %q", test.code, test.want, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ func TestRouterConfigureDuringWorkerHandle(t *testing.T) {
|
|||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for i := 0; i < 300; i++ {
|
for range 300 {
|
||||||
conn := newConn("GET / HTTP/1.1\r\nHost: h\r\n\r\n")
|
conn := newConn("GET / HTTP/1.1\r\nHost: h\r\n\r\n")
|
||||||
conn.Hangup()
|
conn.Hangup()
|
||||||
router.Handle(conn) // Drops are fine, panics are not.
|
router.Handle(conn) // Drops are fine, panics are not.
|
||||||
@@ -367,7 +367,7 @@ func TestRouterConfigureDuringWorkerHandle(t *testing.T) {
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
// Each Configure sleeps 5ms tearing down the previous generation, keep
|
// Each Configure sleeps 5ms tearing down the previous generation, keep
|
||||||
// the count low and let the Handle loop supply the concurrency.
|
// the count low and let the Handle loop supply the concurrency.
|
||||||
for i := 0; i < 20; i++ {
|
for range 20 {
|
||||||
// errBusyExchanges is legitimate backpressure: the previous
|
// errBusyExchanges is legitimate backpressure: the previous
|
||||||
// generation was still serving when the buffers were needed.
|
// generation was still serving when the buffers were needed.
|
||||||
if err := router.Configure(cfg); err != nil && err != errBusyExchanges {
|
if err := router.Configure(cfg); err != nil && err != errBusyExchanges {
|
||||||
|
|||||||
Reference in New Issue
Block a user