aboutsummaryrefslogtreecommitdiff
path: root/server_timing_test.go
diff options
context:
space:
mode:
authorGravatar Aliaksandr Valialkin <valyala@gmail.com> 2015-10-19 01:21:09 +0300
committerGravatar Aliaksandr Valialkin <valyala@gmail.com> 2015-10-19 01:21:09 +0300
commita049630bca2089ac5ade886b2ea5554f65f065d3 (patch)
treed3156cf552338e101108988bce94cd6438742d38 /server_timing_test.go
downloadfasthttp-a049630bca2089ac5ade886b2ea5554f65f065d3.tar.gz
fasthttp-a049630bca2089ac5ade886b2ea5554f65f065d3.tar.bz2
fasthttp-a049630bca2089ac5ade886b2ea5554f65f065d3.zip
initial commit
Diffstat (limited to 'server_timing_test.go')
-rw-r--r--server_timing_test.go337
1 files changed, 337 insertions, 0 deletions
diff --git a/server_timing_test.go b/server_timing_test.go
new file mode 100644
index 0000000..50cc228
--- /dev/null
+++ b/server_timing_test.go
@@ -0,0 +1,337 @@
+package fasthttp
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net"
+ "net/http"
+ "runtime"
+ "sync/atomic"
+ "testing"
+ "time"
+)
+
+func BenchmarkServerGet1ReqPerConn(b *testing.B) {
+ benchmarkServerGet(b, 1)
+}
+
+func BenchmarkServerGet2ReqPerConn(b *testing.B) {
+ benchmarkServerGet(b, 2)
+}
+
+func BenchmarkServerGet10ReqPerConn(b *testing.B) {
+ benchmarkServerGet(b, 10)
+}
+
+func BenchmarkServerGet10000ReqPerConn(b *testing.B) {
+ benchmarkServerGet(b, 10000)
+}
+
+func BenchmarkNetHTTPServerGet1ReqPerConn(b *testing.B) {
+ benchmarkNetHTTPServerGet(b, 1)
+}
+
+func BenchmarkNetHTTPServerGet2ReqPerConn(b *testing.B) {
+ benchmarkNetHTTPServerGet(b, 2)
+}
+
+func BenchmarkNetHTTPServerGet10ReqPerConn(b *testing.B) {
+ benchmarkNetHTTPServerGet(b, 10)
+}
+
+func BenchmarkNetHTTPServerGet10000ReqPerConn(b *testing.B) {
+ benchmarkNetHTTPServerGet(b, 10000)
+}
+
+func BenchmarkServerPost1ReqPerConn(b *testing.B) {
+ benchmarkServerPost(b, 1)
+}
+
+func BenchmarkServerPost2ReqPerConn(b *testing.B) {
+ benchmarkServerPost(b, 2)
+}
+
+func BenchmarkServerPost10ReqPerConn(b *testing.B) {
+ benchmarkServerPost(b, 10)
+}
+
+func BenchmarkServerPost10000ReqPerConn(b *testing.B) {
+ benchmarkServerPost(b, 10000)
+}
+
+func BenchmarkNetHTTPServerPost1ReqPerConn(b *testing.B) {
+ benchmarkNetHTTPServerPost(b, 1)
+}
+
+func BenchmarkNetHTTPServerPost2ReqPerConn(b *testing.B) {
+ benchmarkNetHTTPServerPost(b, 2)
+}
+
+func BenchmarkNetHTTPServerPost10ReqPerConn(b *testing.B) {
+ benchmarkNetHTTPServerPost(b, 10)
+}
+
+func BenchmarkNetHTTPServerPost10000ReqPerConn(b *testing.B) {
+ benchmarkNetHTTPServerPost(b, 10000)
+}
+
+func BenchmarkServerSteal(b *testing.B) {
+ requestsPerConn := 10
+ ch := make(chan struct{}, b.N)
+ n := uint32(0)
+ s := &Server{
+ Handler: func(ctx *ServerCtx) {
+ if atomic.AddUint32(&n, 1)&7 == 0 {
+ ctx.Steal()
+ }
+ ctx.Error("foobar", 123)
+ registerServedRequest(b, ch)
+ },
+ Logger: log.New(ioutil.Discard, "", 0),
+ }
+ req := "GET /foo HTTP/1.1\r\nHost: google.com\r\n\r\n"
+ requestsSent := benchmarkServer(b, &testServer{s}, requestsPerConn, req)
+ verifyRequestsServed(b, requestsSent, ch)
+}
+
+type fakeServerConn struct {
+ net.TCPConn
+
+ addr net.Addr
+ r *io.PipeReader
+ n int
+ nn int
+ next chan struct{}
+ b []byte
+}
+
+func (c *fakeServerConn) Read(b []byte) (int, error) {
+ if c.nn == 0 {
+ return 0, io.EOF
+ }
+ if len(b) > c.nn {
+ b = b[:c.nn]
+ }
+ n, err := c.r.Read(b)
+ c.nn -= n
+ return n, err
+}
+
+func (c *fakeServerConn) Write(b []byte) (int, error) {
+ return len(b), nil
+}
+
+func (c *fakeServerConn) RemoteAddr() net.Addr {
+ return c.addr
+}
+
+func (c *fakeServerConn) Close() error {
+ c.nn = c.n
+ c.next <- struct{}{}
+ return nil
+}
+
+type fakeListener struct {
+ addr net.TCPAddr
+ stop chan struct{}
+ server fakeServerConn
+ client *bufio.Writer
+ w *io.PipeWriter
+
+ v interface{}
+}
+
+func (ln *fakeListener) Accept() (net.Conn, error) {
+ select {
+ case <-ln.stop:
+ return nil, io.EOF
+ case <-ln.server.next:
+ return &ln.server, nil
+ }
+}
+
+func (ln *fakeListener) Close() error {
+ return nil
+}
+
+func (ln *fakeListener) Addr() net.Addr {
+ return &ln.addr
+}
+
+func newFakeListener(bytesPerConn int) *fakeListener {
+ r, w := io.Pipe()
+ c := &fakeListener{
+ stop: make(chan struct{}, 1),
+ w: w,
+ client: bufio.NewWriter(w),
+ }
+ c.server.addr = &c.addr
+ c.server.r = r
+ c.server.n = bytesPerConn
+ c.server.nn = bytesPerConn
+ c.server.b = make([]byte, 1024)
+ c.server.next = make(chan struct{}, 1)
+ c.server.next <- struct{}{}
+ return c
+}
+
+var fakeResponse = []byte("Hello, world!")
+
+func benchmarkServerGet(b *testing.B, requestsPerConn int) {
+ ch := make(chan struct{}, b.N)
+ s := &Server{
+ Handler: func(ctx *ServerCtx) {
+ if !ctx.Request.Header.IsMethodGet() {
+ b.Fatalf("Unexpected request method: %s", ctx.Request.Header.Method)
+ }
+ ctx.Success("text/plain", fakeResponse)
+ registerServedRequest(b, ch)
+ },
+ Logger: log.New(ioutil.Discard, "", 0),
+ }
+ req := "GET /foobar?baz HTTP/1.1\r\nHost: google.com\r\n\r\n"
+ requestsSent := benchmarkServer(b, &testServer{s}, requestsPerConn, req)
+ verifyRequestsServed(b, requestsSent, ch)
+}
+
+func benchmarkNetHTTPServerGet(b *testing.B, requestsPerConn int) {
+ ch := make(chan struct{}, b.N)
+ s := &http.Server{
+ Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ if req.Method != "GET" {
+ b.Fatalf("Unexpected request method: %s", req.Method)
+ }
+ w.Header().Set("Content-Type", "text/plain")
+ w.Write(fakeResponse)
+ registerServedRequest(b, ch)
+ }),
+ }
+ req := "GET /foobar?baz HTTP/1.1\r\nHost: google.com\r\n\r\n"
+ requestsSent := benchmarkServer(b, s, requestsPerConn, req)
+ verifyRequestsServed(b, requestsSent, ch)
+}
+
+func benchmarkServerPost(b *testing.B, requestsPerConn int) {
+ ch := make(chan struct{}, b.N)
+ s := &Server{
+ Handler: func(ctx *ServerCtx) {
+ if !ctx.Request.Header.IsMethodPost() {
+ b.Fatalf("Unexpected request method: %s", ctx.Request.Header.Method)
+ }
+ body := ctx.Request.Body
+ if !bytes.Equal(body, fakeResponse) {
+ b.Fatalf("Unexpected body %q. Expected %q", body, fakeResponse)
+ }
+ ctx.Success("text/plain", body)
+ registerServedRequest(b, ch)
+ },
+ Logger: log.New(ioutil.Discard, "", 0),
+ }
+ req := fmt.Sprintf("POST /foobar?baz HTTP/1.1\r\nHost: google.com\r\nContent-Type: foo/bar\r\nContent-Length: %d\r\n\r\n%s",
+ len(fakeResponse), fakeResponse)
+ requestsSent := benchmarkServer(b, &testServer{s}, requestsPerConn, req)
+ verifyRequestsServed(b, requestsSent, ch)
+}
+
+func benchmarkNetHTTPServerPost(b *testing.B, requestsPerConn int) {
+ ch := make(chan struct{}, b.N)
+ s := &http.Server{
+ Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ if req.Method != "POST" {
+ b.Fatalf("Unexpected request method: %s", req.Method)
+ }
+ body, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ b.Fatalf("Unexpected error: %s", err)
+ }
+ req.Body.Close()
+ if !bytes.Equal(body, fakeResponse) {
+ b.Fatalf("Unexpected body %q. Expected %q", body, fakeResponse)
+ }
+ w.Header().Set("Content-Type", "text/plain")
+ w.Write(body)
+ registerServedRequest(b, ch)
+ }),
+ }
+ req := fmt.Sprintf("POST /foobar?baz HTTP/1.1\r\nHost: google.com\r\nContent-Type: foo/bar\r\nContent-Length: %d\r\n\r\n%s",
+ len(fakeResponse), fakeResponse)
+ requestsSent := benchmarkServer(b, s, requestsPerConn, req)
+ verifyRequestsServed(b, requestsSent, ch)
+}
+
+func registerServedRequest(b *testing.B, ch chan<- struct{}) {
+ select {
+ case ch <- struct{}{}:
+ default:
+ b.Fatalf("More than %d requests served", cap(ch))
+ }
+}
+
+func verifyRequestsServed(b *testing.B, requestsSent uint64, ch <-chan struct{}) {
+ requestsServed := uint64(0)
+ for len(ch) > 0 {
+ <-ch
+ requestsServed++
+ }
+ for requestsServed < requestsSent {
+ select {
+ case <-ch:
+ requestsServed++
+ case <-time.After(100 * time.Millisecond):
+ b.Fatalf("Unexpected number of requests served %d. Expected %d", requestsServed, requestsSent)
+ }
+ }
+}
+
+type realServer interface {
+ Serve(ln net.Listener) error
+}
+
+type testServer struct {
+ *Server
+}
+
+func (s *testServer) Serve(ln net.Listener) error {
+ return s.Server.ServeConcurrency(ln, runtime.NumCPU())
+}
+
+func benchmarkServer(b *testing.B, s realServer, requestsPerConn int, strRequest string) uint64 {
+ request := []byte(strRequest)
+ bytesPerConn := requestsPerConn * len(request)
+
+ requestsSent := uint64(0)
+
+ b.RunParallel(func(pb *testing.PB) {
+ n := uint64(0)
+ ln := newFakeListener(bytesPerConn)
+ ch := make(chan struct{})
+ go func() {
+ s.Serve(ln)
+ ch <- struct{}{}
+ }()
+
+ for pb.Next() {
+ if _, err := ln.client.Write(request); err != nil {
+ b.Fatalf("unexpected error when sending request to conn: %s", err)
+ }
+ n++
+ }
+ if err := ln.client.Flush(); err != nil {
+ b.Fatalf("unexpected error when flushing data: %s", err)
+ }
+
+ ln.w.CloseWithError(io.EOF)
+ ln.stop <- struct{}{}
+ select {
+ case <-ch:
+ case <-time.After(500 * time.Millisecond):
+ b.Fatalf("Server.Serve() didn't stop")
+ }
+ atomic.AddUint64(&requestsSent, n)
+ })
+ return requestsSent
+}