diff --git a/pkg/app/client/client_test.go b/pkg/app/client/client_test.go
index 5f3e8c62f..0b2db0ebc 100644
--- a/pkg/app/client/client_test.go
+++ b/pkg/app/client/client_test.go
@@ -121,6 +121,49 @@ func TestCloseIdleConnections(t *testing.T) {
}
}
+func BenchmarkCloseIdleConnections(b *testing.B) {
+ opt := config.NewOptions([]config.Option{})
+ opt.Addr = "unix-test-10000"
+ opt.Network = "unix"
+ engine := route.NewEngine(opt)
+
+ go engine.Run()
+ defer func() {
+ engine.Close()
+ }()
+ time.Sleep(time.Millisecond * 500)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ c, _ := NewClient(WithDialer(newMockDialerWithCustomFunc(opt.Network, opt.Addr, 1*time.Second, nil)))
+ if _, _, err := c.Get(context.Background(), nil, "http://google.com"); err != nil {
+ b.Fatal(err)
+ }
+ connsLen := func() int {
+ c.mLock.Lock()
+ defer c.mLock.Unlock()
+
+ if _, ok := c.m["google.com"]; !ok {
+ return 0
+ }
+ return c.m["google.com"].ConnectionCount()
+ }
+
+ if conns := connsLen(); conns > 1 {
+ b.Errorf("expected 1 conns got %d", conns)
+ }
+
+ c.CloseIdleConnections()
+
+ if conns := connsLen(); conns > 0 {
+ b.Errorf("expected 0 conns got %d", conns)
+ }
+ }
+ })
+}
+
func TestClientInvalidURI(t *testing.T) {
t.Parallel()
@@ -190,6 +233,45 @@ func TestClientGetWithBody(t *testing.T) {
}
}
+func BenchmarkClientGetWithBody(b *testing.B) {
+ opt := config.NewOptions([]config.Option{})
+ opt.Addr = "unix-test-10002"
+ opt.Network = "unix"
+ engine := route.NewEngine(opt)
+ engine.GET("/", func(c context.Context, ctx *app.RequestContext) {
+ body := ctx.Request.Body()
+ ctx.Write(body) //nolint:errcheck
+ })
+ go engine.Run()
+ defer func() {
+ engine.Close()
+ }()
+ time.Sleep(time.Millisecond * 500)
+
+ c, _ := NewClient(WithDialer(newMockDialerWithCustomFunc(opt.Network, opt.Addr, 1*time.Second, nil)))
+ req, res := protocol.AcquireRequest(), protocol.AcquireResponse()
+ defer func() {
+ protocol.ReleaseRequest(req)
+ protocol.ReleaseResponse(res)
+ }()
+ req.Header.SetMethod(consts.MethodGet)
+ req.SetRequestURI("http://example.com")
+ req.SetBodyString("test")
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ err := c.Do(context.Background(), req, res)
+ if err != nil {
+ b.Fatal(err)
+ }
+ if len(res.Body()) == 0 {
+ b.Fatal("missing request body")
+ }
+ res.Reset()
+ }
+}
+
func TestClientPostBodyStream(t *testing.T) {
t.Parallel()
diff --git a/pkg/app/context_timing_test.go b/pkg/app/context_timing_test.go
new file mode 100644
index 000000000..c95a948e1
--- /dev/null
+++ b/pkg/app/context_timing_test.go
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2022 CloudWeGo Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package app
+
+import (
+ "bytes"
+ "compress/gzip"
+ "compress/zlib"
+ "errors"
+ "io"
+ "testing"
+
+ "github.com/cloudwego/hertz/pkg/common/test/assert"
+)
+
+func BenchmarkNewContext(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ c := NewContext(0)
+ c.Reset()
+ }
+}
+
+// go test -v -run=^$ -bench=BenchmarkCtxJSON -benchmem -count=4
+func BenchmarkCtxJSON(b *testing.B) {
+ ctx := NewContext(0)
+ defer ctx.Reset()
+ type SomeStruct struct {
+ Name string
+ Age uint8
+ }
+ data := SomeStruct{
+ Name: "Grame",
+ Age: 20,
+ }
+ b.ReportAllocs()
+ b.ResetTimer()
+ for n := 0; n < b.N; n++ {
+ ctx.JSON(200, &data)
+ }
+}
+
+func BenchmarkCtxString(b *testing.B) {
+ c := NewContext(0)
+ defer c.Reset()
+ s := "Hello, World!"
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ c.String(200, s)
+ }
+}
+
+// go test -v -run=^$ -bench=BenchmarkCtxBody -benchmem -count=4
+func BenchmarkCtxBody(b *testing.B) {
+ ctx := NewContext(0)
+ defer ctx.Reset()
+ data := []byte("hello world")
+ ctx.Request.SetBodyRaw(data)
+ for n := 0; n < b.N; n++ {
+ _ = ctx.Request.Body()
+ }
+ assert.DeepEqual(b, data, ctx.Request.Body())
+}
+
+// go test -v -run=^$ -bench=BenchmarkCtxBodyWithCompression -benchmem -count=4
+func BenchmarkCtxBodyWithCompression(b *testing.B) {
+ encodingErr := errors.New("failed to encoding data")
+ var (
+ compressGzip = func(data []byte) ([]byte, error) {
+ var buf bytes.Buffer
+ writer := gzip.NewWriter(&buf)
+ if _, err := writer.Write(data); err != nil {
+ return nil, encodingErr
+ }
+ if err := writer.Flush(); err != nil {
+ return nil, encodingErr
+ }
+ if err := writer.Close(); err != nil {
+ return nil, encodingErr
+ }
+ return buf.Bytes(), nil
+ }
+ compressDeflate = func(data []byte) ([]byte, error) {
+ var buf bytes.Buffer
+ writer := zlib.NewWriter(&buf)
+ if _, err := writer.Write(data); err != nil {
+ return nil, encodingErr
+ }
+ if err := writer.Flush(); err != nil {
+ return nil, encodingErr
+ }
+ if err := writer.Close(); err != nil {
+ return nil, encodingErr
+ }
+ return buf.Bytes(), nil
+ }
+ )
+ compressionTests := []struct {
+ contentEncoding string
+ compressWriter func([]byte) ([]byte, error)
+ }{
+ {
+ contentEncoding: "gzip",
+ compressWriter: compressGzip,
+ },
+ {
+ contentEncoding: "gzip,invalid",
+ compressWriter: compressGzip,
+ },
+ {
+ contentEncoding: "deflate",
+ compressWriter: compressDeflate,
+ },
+ {
+ contentEncoding: "gzip,deflate",
+ compressWriter: func(data []byte) ([]byte, error) {
+ var (
+ buf bytes.Buffer
+ writer interface {
+ io.WriteCloser
+ Flush() error
+ }
+ err error
+ )
+ // deflate
+ {
+ writer = zlib.NewWriter(&buf)
+ if _, err = writer.Write(data); err != nil {
+ return nil, encodingErr
+ }
+ if err = writer.Flush(); err != nil {
+ return nil, encodingErr
+ }
+ if err = writer.Close(); err != nil {
+ return nil, encodingErr
+ }
+ }
+
+ data = make([]byte, buf.Len())
+ copy(data, buf.Bytes())
+ buf.Reset()
+
+ // gzip
+ {
+ writer = gzip.NewWriter(&buf)
+ if _, err = writer.Write(data); err != nil {
+ return nil, encodingErr
+ }
+ if err = writer.Flush(); err != nil {
+ return nil, encodingErr
+ }
+ if err = writer.Close(); err != nil {
+ return nil, encodingErr
+ }
+ }
+
+ return buf.Bytes(), nil
+ },
+ },
+ }
+
+ for _, ct := range compressionTests {
+ b.Run(ct.contentEncoding, func(b *testing.B) {
+ c := NewContext(0)
+ defer c.Reset()
+ const input = "john=doe"
+
+ c.Request.Header.Set("Content-Encoding", ct.contentEncoding)
+ compressedBody, err := ct.compressWriter([]byte(input))
+ assert.DeepEqual(b, nil, err)
+
+ c.Request.SetBody(compressedBody)
+ for i := 0; i < b.N; i++ {
+ _ = c.Request.Body()
+ }
+ })
+ }
+}
+
+func BenchmarkCtxWrite(b *testing.B) {
+ c := NewContext(0)
+ byt := []byte("Hello, World!")
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = c.Write(byt)
+ }
+}
+
+func BenchmarkCtxWriteString(b *testing.B) {
+ c := NewContext(0)
+ defer c.Reset()
+ s := "Hello, World!"
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = c.WriteString(s)
+ }
+}
diff --git a/pkg/app/fs_test.go b/pkg/app/fs_test.go
index c4ad0e426..c99ead772 100644
--- a/pkg/app/fs_test.go
+++ b/pkg/app/fs_test.go
@@ -86,6 +86,26 @@ func TestNewVHostPathRewriter(t *testing.T) {
}
}
+func BenchmarkNewVHostPathRewriter(b *testing.B) {
+ var ctx RequestContext
+ var req protocol.Request
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ req.Header.SetHost("foobar.com")
+ req.SetRequestURI("https://aaa.bbb.cc/one/two/three/four?asdf=dsf")
+ req.CopyTo(&ctx.Request)
+
+ f := NewVHostPathRewriter(2)
+ path := f(&ctx)
+
+ expectedPath := "/aaa.bbb.cc/three/four"
+ assert.DeepEqual(b, expectedPath, string(path))
+ ctx.Request.Reset()
+ req.Reset()
+ }
+}
+
func TestNewVHostPathRewriterMaliciousHost(t *testing.T) {
var ctx RequestContext
var req protocol.Request
@@ -295,6 +315,48 @@ func TestServeFileCompressed(t *testing.T) {
}
}
+func BenchmarkServeFileHead(b *testing.B) {
+ var ctx RequestContext
+ var req protocol.Request
+
+ req.Header.SetMethod(consts.MethodHead)
+ req.SetRequestURI("http://foobar.com/baz")
+ req.CopyTo(&ctx.Request)
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ ServeFile(&ctx, "fs.go")
+
+ var r protocol.Response
+ r.SkipBody = true
+ s := resp.GetHTTP1Response(&ctx.Response).String()
+ zr := mock.NewZeroCopyReader(s)
+ if err := resp.Read(&r, zr); err != nil {
+ b.Fatalf("unexpected error: %s", err)
+ }
+
+ ce := r.Header.ContentEncoding()
+ if len(ce) > 0 {
+ b.Fatalf("Unexpected 'Content-Encoding' %q", ce)
+ }
+
+ body := r.Body()
+ if len(body) > 0 {
+ b.Fatalf("unexpected response body %q. Expecting empty body", body)
+ }
+
+ expectedBody, err := getFileContents("/fs.go")
+ if err != nil {
+ b.Fatalf("unexpected error: %s", err)
+ }
+ contentLength := r.Header.ContentLength()
+ if contentLength != len(expectedBody) {
+ b.Fatalf("unexpected Content-Length: %d. expecting %d", contentLength, len(expectedBody))
+ }
+ }
+ b.ReportAllocs()
+}
+
func TestServeFileUncompressed(t *testing.T) {
t.Parallel()
@@ -328,6 +390,42 @@ func TestServeFileUncompressed(t *testing.T) {
}
}
+func BenchmarkServeFileUncompressed(b *testing.B) {
+ var ctx RequestContext
+ var req protocol.Request
+
+ req.SetRequestURI("http://foobar.com/baz")
+ req.Header.Set(consts.HeaderAcceptEncoding, "gzip")
+ req.CopyTo(&ctx.Request)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ ServeFileUncompressed(&ctx, "fs.go")
+
+ var r protocol.Response
+ s := resp.GetHTTP1Response(&ctx.Response).String()
+ zr := mock.NewZeroCopyReader(s)
+ if err := resp.Read(&r, zr); err != nil {
+ b.Fatalf("unexpected error: %s", err)
+ }
+
+ ce := r.Header.ContentEncoding()
+ if len(ce) > 0 {
+ b.Fatalf("Unexpected 'Content-Encoding' %q", ce)
+ }
+
+ body := r.Body()
+ expectedBody, err := getFileContents("/fs.go")
+ if err != nil {
+ b.Fatalf("unexpected error: %s", err)
+ }
+ if !bytes.Equal(body, expectedBody) {
+ b.Fatalf("unexpected body %q. expecting %q", body, expectedBody)
+ }
+ }
+}
+
func TestFSByteRangeConcurrent(t *testing.T) {
t.Parallel()
@@ -462,6 +560,27 @@ func testParseByteRangeSuccess(t *testing.T, v string, contentLength, startPos,
}
}
+func BenchmarkParseByteRange(b *testing.B) {
+ f := func(b *testing.B, v string, contentLength, startPos, endPos int) {
+ startPos1, endPos1, err := ParseByteRange([]byte(v), contentLength)
+ if err != nil {
+ b.Fatalf("unexpected error: %s. v=%q, contentLength=%d", err, v, contentLength)
+ }
+ if startPos1 != startPos {
+ b.Fatalf("unexpected startPos=%d. Expecting %d. v=%q, contentLength=%d", startPos1, startPos, v, contentLength)
+ }
+ if endPos1 != endPos {
+ b.Fatalf("unexpected endPos=%d. Expectind %d. v=%q, contentLength=%d", endPos1, endPos, v, contentLength)
+ }
+ }
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ f(b, "bytes=1234-6789", 6790, 1234, 6789)
+ }
+}
+
func TestParseByteRangeError(t *testing.T) {
t.Parallel()
diff --git a/pkg/app/middlewares/client/sd/discovery_test.go b/pkg/app/middlewares/client/sd/discovery_test.go
index a1c8c8d5f..86f96c830 100644
--- a/pkg/app/middlewares/client/sd/discovery_test.go
+++ b/pkg/app/middlewares/client/sd/discovery_test.go
@@ -55,3 +55,39 @@ func TestDiscovery(t *testing.T) {
_ = mw(checkMdw)(context.Background(), req, resp)
}
}
+
+func BenchmarkDiscovery(b *testing.B) {
+ instances := []discovery.Instance{
+ discovery.NewInstance("tcp", "127.0.0.1:8888", 10, nil),
+ discovery.NewInstance("tcp", "127.0.0.1:8889", 10, nil),
+ }
+ r := &discovery.SynthesizedResolver{
+ TargetFunc: func(ctx context.Context, target *discovery.TargetInfo) string {
+ return target.Host
+ },
+ ResolveFunc: func(ctx context.Context, key string) (discovery.Result, error) {
+ return discovery.Result{CacheKey: "svc1", Instances: instances}, nil
+ },
+ NameFunc: func() string { return b.Name() },
+ }
+
+ midware := Discovery(r)
+ checkMdw := func(ctx context.Context, req *protocol.Request, resp *protocol.Response) (err error) {
+ assert.Assert(b, string(req.Host()) == "127.0.0.1:8888" || string(req.Host()) == "127.0.0.1:8889")
+ return nil
+ }
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ req := protocol.AcquireRequest()
+ resp := protocol.AcquireResponse()
+
+ req.Options().Apply([]config.RequestOption{config.WithSD(true)})
+ req.SetRequestURI("http://service_name")
+ _ = midware(checkMdw)(context.Background(), req, resp)
+
+ protocol.ReleaseRequest(req)
+ protocol.ReleaseResponse(resp)
+ }
+}
diff --git a/pkg/app/server/binding/reflect_internal_test.go b/pkg/app/server/binding/reflect_internal_test.go
index 65dc68fc8..847cbe436 100644
--- a/pkg/app/server/binding/reflect_internal_test.go
+++ b/pkg/app/server/binding/reflect_internal_test.go
@@ -49,6 +49,17 @@ func Test_ReferenceValue(t *testing.T) {
assert.DeepEqual(t, "f1", deFoo1PointerVal.Field(0).Interface().(string))
}
+func BenchmarkReferenceValue(b *testing.B) {
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ for i := 0; i < b.N; i++ {
+ foo1 := foo2{F1: "f1"}
+ foo1Val := reflect.ValueOf(foo1)
+ decoder.ReferenceValue(foo1Val, 5)
+ }
+}
+
func Test_GetNonNilReferenceValue(t *testing.T) {
foo1 := (****foo)(nil)
foo1Val := reflect.ValueOf(foo1)
@@ -88,3 +99,19 @@ func Test_GetFieldValue(t *testing.T) {
t.Errorf("expect can set value, but not")
}
}
+
+func BenchmarkGetFieldValue(b *testing.B) {
+ type bar struct {
+ B1 **fooq
+ }
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ bar1 := (***bar)(nil)
+ parentIdx := []int{0}
+
+ bar1Val := reflect.ValueOf(bar1)
+ decoder.GetFieldValue(bar1Val, parentIdx)
+ }
+}
diff --git a/pkg/app/server/render/html_test.go b/pkg/app/server/render/html_test.go
index d474f1c63..e0a36978d 100644
--- a/pkg/app/server/render/html_test.go
+++ b/pkg/app/server/render/html_test.go
@@ -127,3 +127,25 @@ func TestRenderHTML(t *testing.T) {
assert.DeepEqual(t, []byte("text/html; charset=utf-8"), respDebug.Header.Peek("Content-Type"))
assert.DeepEqual(t, []byte("
Main website
"), respDebug.Body())
}
+
+func BenchmarkRenderHTML(b *testing.B) {
+ resp := &protocol.Response{}
+
+ tmpl := template.Must(template.New("").
+ Delims("{[{", "}]}").
+ Funcs(template.FuncMap{}).
+ ParseFiles("../../../common/testdata/template/index.tmpl"))
+
+ r := &HTMLProduction{Template: tmpl}
+
+ html := r.Instance("index.tmpl", utils.H{
+ "title": "Main website",
+ })
+ html.WriteContentType(resp)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ html.Render(resp)
+ }
+}
diff --git a/pkg/app/server/render/json_test.go b/pkg/app/server/render/json_test.go
index 2c2841b32..65cb6fe2a 100644
--- a/pkg/app/server/render/json_test.go
+++ b/pkg/app/server/render/json_test.go
@@ -74,3 +74,15 @@ func Test_DefaultJSONMarshal(t *testing.T) {
t.Fatal("marshal struct is not equal to the string")
}
}
+
+func BenchmarkDefaultJSONMarshal(b *testing.B) {
+ table := map[string]string{
+ "testA": "hello",
+ "B": "world",
+ }
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ _, _ = jsonMarshalFunc(table)
+ }
+}
diff --git a/pkg/app/server/render/render_test.go b/pkg/app/server/render/render_test.go
index 0669cb48c..25ddaf2be 100644
--- a/pkg/app/server/render/render_test.go
+++ b/pkg/app/server/render/render_test.go
@@ -93,6 +93,21 @@ func TestRenderJSON(t *testing.T) {
assert.DeepEqual(t, []byte(consts.MIMEApplicationJSONUTF8), resp.Header.Peek("Content-Type"))
}
+func BenchmarkRenderJSON(b *testing.B) {
+ resp := &protocol.Response{}
+ data := map[string]interface{}{
+ "foo": "bar",
+ "html": "",
+ }
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ for i := 0; i < b.N; i++ {
+ err := (JSONRender{data}).Render(resp)
+ assert.Nil(b, err)
+ }
+}
+
func TestRenderJSONError(t *testing.T) {
resp := &protocol.Response{}
data := make(chan int)
@@ -120,6 +135,21 @@ func TestRenderPureJSON(t *testing.T) {
assert.DeepEqual(t, []byte(consts.MIMEApplicationJSONUTF8), resp.Header.Peek("Content-Type"))
}
+func BenchmarkRenderPureJSON(b *testing.B) {
+ resp := &protocol.Response{}
+ data := map[string]interface{}{
+ "foo": "bar",
+ "html": "",
+ }
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ err := (PureJSON{data}).Render(resp)
+ assert.Nil(b, err)
+ }
+}
+
func TestRenderPureJSONError(t *testing.T) {
resp := &protocol.Response{}
data := make(chan int)
@@ -143,6 +173,18 @@ func TestRenderProtobuf(t *testing.T) {
assert.DeepEqual(t, []byte("application/x-protobuf"), resp.Header.Peek("Content-Type"))
}
+func BenchmarkRenderProtobuf(b *testing.B) {
+ resp := &protocol.Response{}
+ data := proto.TestStruct{Body: []byte("Hello World")}
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ err := (ProtoBuf{&data}).Render(resp)
+ assert.Nil(b, err)
+ }
+}
+
func TestRenderProtobufError(t *testing.T) {
resp := &protocol.Response{}
data := proto.Test{}
@@ -171,6 +213,17 @@ func TestRenderString(t *testing.T) {
assert.DeepEqual(t, []byte(consts.MIMETextPlainUTF8), resp.Header.Peek("Content-Type"))
}
+func BenchmarkRenderString(b *testing.B) {
+ resp := &protocol.Response{}
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ err := (String{Format: "hola %s %d", Data: []interface{}{"manu", 2}}).Render(resp)
+ assert.Nil(b, err)
+ }
+}
+
func TestRenderStringLenZero(t *testing.T) {
resp := &protocol.Response{}
diff --git a/pkg/common/adaptor/request_test.go b/pkg/common/adaptor/request_test.go
index ba3ec6ad3..cfa322866 100644
--- a/pkg/common/adaptor/request_test.go
+++ b/pkg/common/adaptor/request_test.go
@@ -68,6 +68,37 @@ func TestCompatResponse_WriteHeader(t *testing.T) {
makeACall(t, http.MethodPost, testUrl2, testHeader, testBody, consts.StatusOK, []byte(testCookieValue))
}
+func BenchmarkGetCompatRequest(b *testing.B) {
+ var req protocol.Request
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ req.SetMethod("GET")
+ req.SetRequestURI("127.0.0.1")
+ req.SetBody([]byte("foo.com"))
+
+ GetCompatRequest(&req)
+ req.Reset()
+ }
+}
+
+func BenchmarkGetCompatResponseWriter(b *testing.B) {
+ var resp protocol.Response
+
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ for i := 0; i < b.N; i++ {
+ resp.SetBody([]byte("foo.com"))
+ resp.SetStatusCode(200)
+ resp.Header.Set("foo", "bzz")
+ GetCompatResponseWriter(&resp)
+
+ resp.Reset()
+ }
+}
+
func makeACall(t *testing.T, method, url string, header http.Header, body string, expectStatusCode int, expectCookieValue []byte) {
client := http.Client{}
req, _ := http.NewRequest(method, url, strings.NewReader(body))
diff --git a/pkg/common/compress/compress_test.go b/pkg/common/compress/compress_test.go
index 301412441..5c814206a 100644
--- a/pkg/common/compress/compress_test.go
+++ b/pkg/common/compress/compress_test.go
@@ -102,6 +102,15 @@ func TestCompressAppendGzipBytesLevel(t *testing.T) {
}
}
+func BenchmarkAppendGzipBytesLevel(b *testing.B) {
+ dst1 := []byte("")
+ src1 := []byte("hello")
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ AppendGzipBytesLevel(dst1, src1, 5)
+ }
+}
+
func TestCompressWriteGzipLevel(t *testing.T) {
// test default case for WriteGzipLevel
var w defaultByteWriter
@@ -119,6 +128,16 @@ func TestCompressWriteGzipLevel(t *testing.T) {
}
}
+func BenchmarkCompressWriteGzipLevel(b *testing.B) {
+ var w defaultByteWriter
+ p := []byte("hello")
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ WriteGzipLevel(&w, p, 5)
+ w.Reset()
+ }
+}
+
type defaultByteWriter struct {
b []byte
}
@@ -127,3 +146,7 @@ func (w *defaultByteWriter) Write(p []byte) (int, error) {
w.b = append(w.b, p...)
return len(p), nil
}
+
+func (w *defaultByteWriter) Reset() {
+ w.b = w.b[:0]
+}
diff --git a/pkg/common/timer/timer_test.go b/pkg/common/timer/timer_test.go
index 21d77e648..d01dda423 100644
--- a/pkg/common/timer/timer_test.go
+++ b/pkg/common/timer/timer_test.go
@@ -101,3 +101,28 @@ func TestTimerReleaseTimer(t *testing.T) {
t.Fatalf("Expecting the timer is released.")
}
}
+
+func BenchmarkAcquireTimer(b *testing.B) {
+ // run the AcquireTimer function b.N times
+ b.ResetTimer()
+ b.ReportAllocs()
+ for n := 0; n < b.N; n++ {
+ t := AcquireTimer(time.Second)
+ ReleaseTimer(t) // release the timer after acquiring it
+ }
+}
+
+func BenchmarkReleaseTimer(b *testing.B) {
+ // create a slice of timers to be released
+ timers := make([]*time.Timer, b.N)
+ for i := 0; i < b.N; i++ {
+ timers[i] = AcquireTimer(time.Second)
+ }
+
+ // run the ReleaseTimer function b.N times
+ b.ResetTimer() // reset the timer to exclude the time spent on acquiring timers
+ b.ReportAllocs()
+ for n := 0; n < b.N; n++ {
+ ReleaseTimer(timers[n])
+ }
+}
diff --git a/pkg/common/utils/chunk_test.go b/pkg/common/utils/chunk_test.go
index d9c8570b0..128a9a0e5 100644
--- a/pkg/common/utils/chunk_test.go
+++ b/pkg/common/utils/chunk_test.go
@@ -17,6 +17,7 @@
package utils
import (
+ "io"
"testing"
"github.com/cloudwego/hertz/pkg/common/test/assert"
@@ -97,3 +98,105 @@ func TestChunkReadFalseCRLF(t *testing.T) {
err := SkipCRLF(zr)
assert.DeepEqual(t, errBrokenChunk, err)
}
+
+// mockReader is a mock implementation of network.Reader interface
+type mockReader struct {
+ data []byte
+ pos int
+}
+
+func (r *mockReader) Release() error {
+ r.pos = 0
+ r.data = r.data[:0]
+ return nil
+}
+
+func (r *mockReader) Len() int {
+ return len(r.data)
+}
+
+func (r *mockReader) ReadBinary(n int) (p []byte, err error) {
+ return
+}
+
+func (r *mockReader) Read(p []byte) (int, error) {
+ if r.pos >= len(r.data) {
+ return 0, io.EOF
+ }
+ n := copy(p, r.data[r.pos:])
+ r.pos += n
+ return n, nil
+}
+
+func (r *mockReader) ReadByte() (byte, error) {
+ if r.pos >= len(r.data) {
+ return 0, io.EOF
+ }
+ b := r.data[r.pos]
+ r.pos++
+ return b, nil
+}
+
+func (r *mockReader) Peek(n int) ([]byte, error) {
+ if r.pos+n > len(r.data) {
+ return nil, io.EOF
+ }
+ return r.data[r.pos : r.pos+n], nil
+}
+
+func (r *mockReader) Skip(n int) error {
+ if r.pos+n > len(r.data) {
+ return io.EOF
+ }
+ r.pos += n
+ return nil
+}
+
+// BenchmarkParseChunkSize benchmarks the ParseChunkSize function with different inputs
+func BenchmarkParseChunkSize(b *testing.B) {
+ // create a slice of mock readers with different chunk sizes
+ readers := []*mockReader{
+ {data: []byte("1\r\n")},
+ {data: []byte("10\r\n")},
+ {data: []byte("100\r\n")},
+ {data: []byte("1000\r\n")},
+ {data: []byte("10000\r\n")},
+ {data: []byte("100000\r\n")},
+ {data: []byte("1000000\r\n")},
+ }
+
+ // run the ParseChunkSize function b.N times for each reader
+ for _, r := range readers {
+ b.Run(string(r.data), func(b *testing.B) {
+ b.ResetTimer()
+ b.ReportAllocs()
+ for n := 0; n < b.N; n++ {
+ ParseChunkSize(r)
+ r.pos = 0 // reset the reader position
+ }
+ })
+ }
+}
+
+// BenchmarkSkipCRLF benchmarks the SkipCRLF function with different inputs
+func BenchmarkSkipCRLF(b *testing.B) {
+ // create a slice of mock readers with different data
+ readers := []*mockReader{
+ {data: []byte("\r\n")},
+ {data: []byte("foo\r\n")},
+ {data: []byte("bar\r\n")},
+ {data: []byte("baz\r\n")},
+ }
+
+ // run the SkipCRLF function b.N times for each reader
+ for _, r := range readers {
+ b.Run(string(r.data), func(b *testing.B) {
+ b.ResetTimer()
+ b.ReportAllocs()
+ for n := 0; n < b.N; n++ {
+ SkipCRLF(r)
+ r.pos = 0 // reset the reader position
+ }
+ })
+ }
+}
diff --git a/pkg/common/utils/ioutil_test.go b/pkg/common/utils/ioutil_test.go
index e9a573b47..86ced3843 100644
--- a/pkg/common/utils/ioutil_test.go
+++ b/pkg/common/utils/ioutil_test.go
@@ -95,7 +95,7 @@ func newTestReaderForm(r io.ReaderFrom) readerTest {
func TestIoutilCopyBuffer(t *testing.T) {
var writeBuffer bytes.Buffer
- str := string("hertz is very good!!!")
+ str := "hertz is very good!!!"
src := bytes.NewBufferString(str)
dst := network.NewWriter(&writeBuffer)
var buf []byte
@@ -125,6 +125,22 @@ func TestIoutilCopyBuffer(t *testing.T) {
assert.DeepEqual(t, []byte(str[:limit]), writeBuffer.Bytes())
}
+func BenchmarkCopyBuffer(b *testing.B) {
+ var buf []byte
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ var writeBuffer bytes.Buffer
+ str := "hertz is very good!!!"
+ src := bytes.NewBufferString(str)
+ dst := network.NewWriter(&writeBuffer)
+
+ CopyBuffer(dst, src, buf)
+ buf = buf[:0]
+ }
+}
+
func TestIoutilCopyBufferWithIoWriter(t *testing.T) {
var writeBuffer bytes.Buffer
str := "hertz is very good!!!"
@@ -234,6 +250,27 @@ func TestIoutilCopyZeroAlloc(t *testing.T) {
assert.DeepEqual(t, []byte(""), writeBuffer.Bytes())
}
+func BenchmarkCopyZeroAlloc(b *testing.B) {
+ var writeBuffer bytes.Buffer
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ str := "hertz is very good!!!"
+ src := bytes.NewBufferString(str)
+ dst := network.NewWriter(&writeBuffer)
+ srcLen := int64(src.Len())
+
+ written, err := CopyZeroAlloc(dst, src)
+ assert.DeepEqual(b, written, srcLen)
+ assert.DeepEqual(b, err, nil)
+ assert.DeepEqual(b, []byte(str), writeBuffer.Bytes())
+
+ writeBuffer.Reset()
+ dst.Flush()
+ }
+}
+
func TestIoutilCopyBufferWithEmptyBuffer(t *testing.T) {
var writeBuffer bytes.Buffer
str := "hertz is very good!!!"
diff --git a/pkg/common/utils/path_test.go b/pkg/common/utils/path_test.go
index b98a8c747..bf03ed701 100644
--- a/pkg/common/utils/path_test.go
+++ b/pkg/common/utils/path_test.go
@@ -89,6 +89,28 @@ func TestPathCleanPath(t *testing.T) {
assert.DeepEqual(t, expectedPath, cleanedPath)
}
+func BenchmarkCleanPath(b *testing.B) {
+ inputs := []string{
+ "/path/to/some/directory",
+ "/path/../to/../some/directory",
+ "/a/b/c/../../d",
+ "/../a/b/c",
+ "/a/b/c/",
+ "",
+ }
+
+ for _, input := range inputs {
+ b.Run(input, func(b *testing.B) {
+ // Run the CleanPath function b.N times
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ _ = CleanPath(input)
+ }
+ })
+ }
+}
+
// The Function AddMissingPort can only add the missed port, don't consider the other error case.
func TestPathAddMissingPort(t *testing.T) {
ipList := []string{"127.0.0.1", "111.111.1.1", "[0:0:0:0:0:ffff:192.1.56.10]", "[0:0:0:0:0:ffff:c0a8:101]", "www.foobar.com"}
diff --git a/pkg/common/utils/utils_test.go b/pkg/common/utils/utils_test.go
index 92873b51d..2d6c6b036 100644
--- a/pkg/common/utils/utils_test.go
+++ b/pkg/common/utils/utils_test.go
@@ -129,7 +129,7 @@ func TestUtilsNextLine(t *testing.T) {
singleHeaderStrWithFirstNewLine := []byte("\nContent-Type: application/x-www-form-urlencoded")
firstStr, secondStr, sErr := NextLine(singleHeaderStrWithFirstNewLine)
assert.DeepEqual(t, nil, sErr)
- assert.DeepEqual(t, string(""), string(firstStr))
+ assert.DeepEqual(t, "", string(firstStr))
assert.DeepEqual(t, "Content-Type: application/x-www-form-urlencoded", string(secondStr))
singleHeaderStr := []byte("Content-Type: application/x-www-form-urlencoded")
diff --git a/pkg/network/writer_test.go b/pkg/network/writer_test.go
index 4cf76831a..a68b13edf 100644
--- a/pkg/network/writer_test.go
+++ b/pkg/network/writer_test.go
@@ -86,6 +86,35 @@ func TestConvertNetworkWriter(t *testing.T) {
assert.DeepEqual(t, size1K*14+1, iw.WriteNum)
}
+func BenchmarkMalloc1K(b *testing.B) {
+ iw := &mockIOWriter{}
+ w := NewWriter(iw)
+ nw, _ := w.(*networkWriter)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ buf, _ := nw.Malloc(size1K)
+ assert.DeepEqual(b, len(buf), size1K)
+ }
+}
+
+func BenchmarkWriteBinary1K(b *testing.B) {
+ iw := &mockIOWriter{}
+ w := NewWriter(iw)
+ nw, _ := w.(*networkWriter)
+
+ nw.Malloc(size1K * 6)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ b := make([]byte, size1K)
+ nw.WriteBinary(b)
+ nw.Flush()
+ }
+}
+
type mockIOWriter struct {
WriteNum int
}
diff --git a/pkg/protocol/args_test.go b/pkg/protocol/args_test.go
index 0e89f30cc..a8c6e54b4 100644
--- a/pkg/protocol/args_test.go
+++ b/pkg/protocol/args_test.go
@@ -61,6 +61,16 @@ func TestArgsDeleteAll(t *testing.T) {
}
}
+func BenchmarkArgs_Add(b *testing.B) {
+ var a Args
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ a.Add("q1", "foo")
+ }
+}
+
func TestArgsBytesOperation(t *testing.T) {
var a Args
a.Add("q1", "foo")
@@ -71,6 +81,19 @@ func TestArgsBytesOperation(t *testing.T) {
assert.DeepEqual(t, []byte(""), peekArgBytes(a.args, []byte("q2")))
}
+func BenchmarkArgs_setArgBytes(b *testing.B) {
+ var a Args
+ a.Add("q1", "foo")
+ a.Add("q2", "bar")
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ setArgBytes(a.args, a.args[0].key, a.args[0].value, false)
+ setArgBytes(a.args, a.args[1].key, a.args[1].value, true)
+ }
+}
+
func TestArgsPeekExists(t *testing.T) {
var a Args
a.Add("q1", "foo")
@@ -90,6 +113,20 @@ func TestArgsPeekExists(t *testing.T) {
assert.True(t, b4)
}
+func BenchmarkArgs_PeekExists(b *testing.B) {
+ var a Args
+ a.Add("q1", "foo")
+ a.Add("", "")
+ a.Add("?", "=")
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ a.PeekExists("q1")
+ a.PeekExists("")
+ a.PeekExists("?")
+ }
+}
+
func TestSetArg(t *testing.T) {
a := Args{args: setArg(nil, "q1", "foo", true)}
a.Add("", "")
@@ -119,6 +156,21 @@ func TestArgsParseBytes(t *testing.T) {
assert.DeepEqual(t, &ta2, &a2)
}
+func BenchmarkArgs_ParseBytes(b *testing.B) {
+ var ta1 Args
+ ta1.Add("q1", "foo")
+ ta1.Add("q1", "bar")
+ ta1.Add("q2", "123")
+ ta1.Add("q3", "")
+ var a1 Args
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ a1.ParseBytes([]byte("q1=foo&q1=bar&q2=123&q3="))
+ a1.Reset()
+ }
+}
+
func TestArgsVisitAll(t *testing.T) {
var a Args
var s []string
@@ -130,6 +182,17 @@ func TestArgsVisitAll(t *testing.T) {
assert.DeepEqual(t, []string{"cloudwego", "hertz", "hello", "world"}, s)
}
+func BenchmarkArgs_VisitAll(b *testing.B) {
+ var a Args
+ a.Add("cloudwego", "hertz")
+ a.Add("hello", "world")
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ a.VisitAll(func(key, value []byte) {})
+ }
+}
+
func TestArgsPeekMulti(t *testing.T) {
var a Args
a.Add("cloudwego", "hertz")
@@ -152,3 +215,16 @@ func TestArgsPeekMulti(t *testing.T) {
expectedVV = [][]byte{[]byte("world")}
assert.DeepEqual(t, expectedVV, vv)
}
+
+func BenchmarkArgs_PeekAll(b *testing.B) {
+ var a Args
+ a.Add("cloudwego", "hertz")
+ a.Add("cloudwego", "kitex")
+ a.Add("cloudwego", "")
+ a.Add("hello", "world")
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ a.PeekAll("cloudwego")
+ }
+}
diff --git a/pkg/protocol/cookie_test.go b/pkg/protocol/cookie_test.go
index f7637e540..0d4c6bae8 100644
--- a/pkg/protocol/cookie_test.go
+++ b/pkg/protocol/cookie_test.go
@@ -78,6 +78,18 @@ func testCookieAppendBytes(t *testing.T, c *Cookie, key, value, expectedS string
}
}
+func BenchmarkCookieAppendBytes(b *testing.B) {
+ c := &Cookie{}
+ c.SetKey("xxx")
+ c.SetValue("yyy")
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ c.AppendBytes(nil)
+ }
+}
+
func TestParseRequestCookies(t *testing.T) {
t.Parallel()
@@ -99,6 +111,16 @@ func testParseRequestCookies(t *testing.T, s, expectedS string) {
}
}
+func BenchmarkParseRequestCookies(b *testing.B) {
+ s := "xxx=aa; bb=c; d; e=g"
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ parseRequestCookies(nil, []byte(s))
+ }
+}
+
func TestAppendRequestCookieBytes(t *testing.T) {
t.Parallel()
@@ -133,6 +155,22 @@ func testAppendRequestCookieBytes(t *testing.T, s, expectedS string) {
}
}
+func BenchmarkAppendRequestCookieBytes(b *testing.B) {
+ cookies := make([]argsKV, 0)
+ c := argsKV{
+ key: []byte("fff"),
+ value: []byte("yyy"),
+ }
+ cookies = append(cookies, c)
+ prefix := []byte("foobar")
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ appendRequestCookieBytes(prefix, cookies)
+ }
+}
+
func TestCookieSecureHTTPOnly(t *testing.T) {
t.Parallel()
@@ -341,6 +379,16 @@ func TestCookieParse(t *testing.T) {
"xxx=yyy; expires=Tue, 10 Nov 2009 23:00:00 GMT; domain=foobar.com; path=/a/b")
}
+func BenchmarkCookie_Parse(b *testing.B) {
+ var c Cookie
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ _ = c.Parse(" xxx = yyy ; path=/a/b;;;domain=foobar.com ; expires= Tue, 10 Nov 2009 23:00:00 GMT ; ;;xxx=yyy; expires=Tue, 10 Nov 2009 23:00:00 GMT; domain=foobar.com; path=/a/b")
+ }
+}
+
func Test_decodeCookieArg(t *testing.T) {
src := []byte(" \"aaaaabbbbb\" ")
dst := make([]byte, 0)
diff --git a/pkg/protocol/header_timing_test.go b/pkg/protocol/header_timing_test.go
index 1f4897a1a..a740f0269 100644
--- a/pkg/protocol/header_timing_test.go
+++ b/pkg/protocol/header_timing_test.go
@@ -44,7 +44,11 @@ package protocol
import (
"net/http"
"strconv"
+ "strings"
"testing"
+
+ "github.com/cloudwego/hertz/pkg/common/test/assert"
+ "github.com/cloudwego/hertz/pkg/protocol/consts"
)
func BenchmarkHTTPHeaderGet(b *testing.B) {
@@ -104,3 +108,80 @@ func BenchmarkRefreshServerDate(b *testing.B) {
refreshServerDate()
}
}
+
+func BenchmarkRequestHeaderCopyTo(b *testing.B) {
+ h := new(RequestHeader)
+ h.Add(consts.HeaderContentType, "aaa/bbb")
+ h.Add(consts.HeaderContentEncoding, "gzip")
+ h.Add(consts.HeaderConnection, "close")
+ h.Add(consts.HeaderContentLength, "1234")
+ h.Add(consts.HeaderServer, "aaaa")
+ h.Add(consts.HeaderSetCookie, "cccc")
+ reqHeader := new(RequestHeader)
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ h.CopyTo(reqHeader)
+ }
+}
+
+func BenchmarkResponseHeaderCopyTo(b *testing.B) {
+ h := new(ResponseHeader)
+ h.Add(consts.HeaderContentType, "aaa/bbb")
+ h.Add(consts.HeaderContentEncoding, "gzip")
+ h.Add(consts.HeaderConnection, "close")
+ h.Add(consts.HeaderContentLength, "1234")
+ h.Add(consts.HeaderServer, "aaaa")
+ h.Add(consts.HeaderSetCookie, "cccc")
+ respHeader := new(ResponseHeader)
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ h.CopyTo(respHeader)
+ }
+}
+
+func Benchmark_peekRawHeader(b *testing.B) {
+ s := "Expect: 100-continue\r\nUser-Agent: foo\r\nHost: 127.0.0.1\r\nConnection: Keep-Alive\r\nContent-Length: 5\r\nContent-Type: foo/bar\r\n\r\nabcdef4343"
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ peekRawHeader([]byte(s), []byte("Host"))
+ }
+}
+
+func BenchmarkResponseHeader_SetContentLength(b *testing.B) {
+ rh := new(ResponseHeader)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ rh.SetContentLength(-1)
+ assert.True(b, strings.Contains(string(rh.Header()), "Transfer-Encoding: chunked"))
+ rh.SetContentLength(-2)
+ assert.True(b, strings.Contains(string(rh.Header()), "Transfer-Encoding: identity"))
+ rh.Reset()
+ }
+}
+
+func BenchmarkRequestHeaderVisitAll(b *testing.B) {
+ h := RequestHeader{}
+ h.Set("xxx", "yyy")
+ h.Set("xxx2", "yyy2")
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ h.VisitAll(func(k, v []byte) {
+ key := string(k)
+ value := string(v)
+ if key != "Xxx" && key != "Xxx2" {
+ b.Fatalf("Unexpected %v. Expected %v", key, "xxx or yyy")
+ }
+ if key == "Xxx" && value != "yyy" {
+ b.Fatalf("Unexpected %v. Expected %v", value, "yyy")
+ }
+ if key == "Xxx2" && value != "yyy2" {
+ b.Fatalf("Unexpected %v. Expected %v", value, "yyy2")
+ }
+ })
+ }
+}
diff --git a/pkg/protocol/http1/ext/common_test.go b/pkg/protocol/http1/ext/common_test.go
index 9bc1936e3..eec41d127 100644
--- a/pkg/protocol/http1/ext/common_test.go
+++ b/pkg/protocol/http1/ext/common_test.go
@@ -123,6 +123,23 @@ func TestReadRawHeaders(t *testing.T) {
assert.DeepEqual(t, s[:index], string(rawHeaders))
}
+func BenchmarkReadRawHeaders(b *testing.B) {
+ s := "HTTP/1.1 200 OK\r\n" +
+ "EmptyValue1:\r\n" +
+ "Content-Type: foo/bar;\r\n\tnewline;\r\n another/newline\r\n" +
+ "Foo: Bar\r\n" +
+ "Multi-Line: one;\r\n two\r\n" +
+ "Values: v1;\r\n v2; v3;\r\n v4;\tv5\r\n" +
+ "Content-Length: 5\r\n\r\n" +
+ "HELLOaaa"
+
+ var dst []byte
+ for i := 0; i < b.N; i++ {
+ ReadRawHeaders(dst, []byte(s))
+ dst = dst[:0]
+ }
+}
+
func TestBodyChunked(t *testing.T) {
var log bytes.Buffer
hlog.SetOutput(&log)
@@ -145,6 +162,21 @@ func TestBodyChunked(t *testing.T) {
assert.DeepEqual(t, 0, log.Len())
}
+func BenchmarkWriteBodyChunked(b *testing.B) {
+ var log bytes.Buffer
+ hlog.SetOutput(&log)
+
+ body := "foobar baz aaa bbb ccc"
+ by := bytes.NewBufferString(body)
+
+ var w bytes.Buffer
+ for i := 0; i < b.N; i++ {
+ zw := netpoll.NewWriter(&w)
+ WriteBodyChunked(zw, by)
+ w.Reset()
+ }
+}
+
func TestBrokenBodyChunked(t *testing.T) {
brokenReader := mock.NewBrokenConn("")
var log bytes.Buffer
@@ -175,6 +207,20 @@ func TestBodyFixedSize(t *testing.T) {
assert.DeepEqual(t, body, rb)
}
+func BenchmarkWriteBodyFixedSize(b *testing.B) {
+ body := mock.CreateFixedBody(10)
+ by := bytes.NewBuffer(body)
+
+ var w bytes.Buffer
+ zw := netpoll.NewWriter(&w)
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ WriteBodyFixedSize(zw, by, int64(len(body)))
+ by.Reset()
+ }
+}
+
func TestBodyFixedSizeQuickPath(t *testing.T) {
conn := mock.NewBrokenConn("")
err := WriteBodyFixedSize(conn.Writer(), conn, 0)
diff --git a/pkg/protocol/http1/ext/headerscanner_test.go b/pkg/protocol/http1/ext/headerscanner_test.go
index 0f8874d81..87bd0d8b0 100644
--- a/pkg/protocol/http1/ext/headerscanner_test.go
+++ b/pkg/protocol/http1/ext/headerscanner_test.go
@@ -112,3 +112,27 @@ func testTestHeaderScannerError(t *testing.T, rawHeaders string, expectError err
assert.NotNil(t, hs.Err)
assert.True(t, errors.Is(hs.Err, expectError))
}
+
+func BenchmarkHeaderScanner_Next(b *testing.B) {
+ firstLine := "HTTP/1.1 200 OK\r\n"
+ rawHeaders := "EmptyValue1:\r\n" +
+ "Content-Type: foo/bar;\r\n\tnewline;\r\n another/newline\r\n" +
+ "Foo: Bar\r\n" +
+ "Multi-Line: one;\r\n two\r\n" +
+ "Values: v1;\r\n v2; v3;\r\n v4;\tv5\r\n" +
+ "\r\n"
+
+ // compared with http response
+ response, err := http.ReadResponse(bufio.NewReader(strings.NewReader(firstLine+rawHeaders)), nil)
+ assert.Nil(b, err)
+ defer func() { response.Body.Close() }()
+
+ hs := &HeaderScanner{}
+ hs.B = []byte(rawHeaders)
+ hs.DisableNormalizing = false
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for hs.Next() {
+ }
+ }
+}
diff --git a/pkg/protocol/http1/req/header_test.go b/pkg/protocol/http1/req/header_test.go
index d0978b3c4..1073ba0fa 100644
--- a/pkg/protocol/http1/req/header_test.go
+++ b/pkg/protocol/http1/req/header_test.go
@@ -82,6 +82,16 @@ func TestRequestHeader_Read(t *testing.T) {
assert.DeepEqual(t, []byte("100-continue"), rh.Peek("Expect"))
}
+func BenchmarkRequestHeaderRead(b *testing.B) {
+ s := "PUT /foo/bar HTTP/1.1\r\nExpect: 100-continue\r\nUser-Agent: foo\r\nHost: 127.0.0.1\r\nConnection: Keep-Alive\r\nContent-Length: 5\r\nContent-Type: foo/bar\r\n\r\nabcdef4343"
+ zr := mock.NewZeroCopyReader(s)
+ rh := protocol.RequestHeader{}
+ for i := 0; i < b.N; i++ {
+ ReadHeader(&rh, zr)
+ rh.Reset()
+ }
+}
+
func TestRequestHeaderMultiLineValue(t *testing.T) {
s := "HTTP/1.1 200 OK\r\n" +
"EmptyValue1:\r\n" +
diff --git a/pkg/protocol/http1/req/request_test.go b/pkg/protocol/http1/req/request_test.go
index 0411187a5..c130aae46 100644
--- a/pkg/protocol/http1/req/request_test.go
+++ b/pkg/protocol/http1/req/request_test.go
@@ -96,6 +96,21 @@ func TestRequestContinueReadBody(t *testing.T) {
}
}
+func BenchmarkRequest_ContinueReadBody(b *testing.B) {
+ s := "PUT /foo/bar HTTP/1.1\r\nExpect: 100-continue\r\nContent-Length: 5\r\nContent-Type: foo/bar\r\n\r\nabcdef4343"
+ zr := mock.NewZeroCopyReader(s)
+
+ var r protocol.Request
+ err := Read(&r, zr)
+ assert.Nil(b, err)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ ContinueReadBody(&r, zr, 0, true)
+ }
+}
+
func TestRequestReadNoBody(t *testing.T) {
t.Parallel()
@@ -136,6 +151,19 @@ func TestRequestRead(t *testing.T) {
}
}
+func BenchmarkRequest_Read(b *testing.B) {
+ var r protocol.Request
+
+ s := "POST / HTTP/1.1\r\n\r\n"
+ zr := mock.NewZeroCopyReader(s)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ _ = Read(&r, zr)
+ }
+}
+
func TestRequestReadNoBodyStreaming(t *testing.T) {
t.Parallel()
@@ -156,6 +184,21 @@ func TestRequestReadNoBodyStreaming(t *testing.T) {
}
}
+func BenchmarkRequest_ContinueReadBodyStream(b *testing.B) {
+ var r protocol.Request
+ r.Header.SetContentLength(-2)
+ r.Header.SetMethod("GET")
+ s := ""
+ zr := mock.NewZeroCopyReader(s)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ for i := 0; i < b.N; i++ {
+ _ = ContinueReadBodyStream(&r, zr, 2048, true)
+ }
+}
+
func TestRequestReadStreaming(t *testing.T) {
t.Parallel()
@@ -411,6 +454,20 @@ func TestChunkedUnexpectedEOF(t *testing.T) {
}
}
+func BenchmarkExtReadBody(b *testing.B) {
+ body := mock.CreateFixedBody(3 * 1024 * 1024)
+ expectedTrailer := map[string]string{"Foo": "chunked shit"}
+ chunkedBody := mock.CreateChunkedBody(body, expectedTrailer, true)
+
+ zr := mock.NewZeroCopyReader(string(chunkedBody))
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ _, _ = ext.ReadBody(zr, -1, 0, nil)
+ }
+}
+
func TestReadBodyChunked(t *testing.T) {
t.Parallel()
diff --git a/pkg/protocol/http1/resp/header_test.go b/pkg/protocol/http1/resp/header_test.go
index 0fad02394..d70882b8e 100644
--- a/pkg/protocol/http1/resp/header_test.go
+++ b/pkg/protocol/http1/resp/header_test.go
@@ -43,6 +43,7 @@ package resp
import (
"bytes"
+ "fmt"
"testing"
"github.com/cloudwego/hertz/pkg/protocol"
@@ -179,3 +180,51 @@ func equalCookie(c1, c2 *protocol.Cookie) bool {
}
return true
}
+
+func BenchmarkSetCookie(b *testing.B) {
+ var h protocol.ResponseHeader
+ var c protocol.Cookie
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ c.SetKey("foobar")
+ c.SetValue("aaa")
+ c.SetDomain("foobar.com")
+
+ h.SetCookie(&c)
+ c.Reset()
+ }
+}
+
+func BenchmarkDelCookie(b *testing.B) {
+ var h protocol.ResponseHeader
+ var c protocol.Cookie
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ c.SetKey("foobar")
+ c.SetValue("aaa")
+ h.SetCookie(&c)
+
+ h.DelCookie("foobar")
+ }
+}
+
+func BenchmarkVisitAllCookie(b *testing.B) {
+ var h protocol.ResponseHeader
+ var c protocol.Cookie
+
+ for i := 0; i < b.N; i++ {
+ c.SetKey(fmt.Sprintf("foobar%v", i))
+ c.SetValue("aaa")
+ c.SetDomain("foobar.com")
+ h.SetCookie(&c)
+ }
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ h.VisitAllCookie(func(k, v []byte) {})
+ }
+}
diff --git a/pkg/protocol/http1/resp/response_test.go b/pkg/protocol/http1/resp/response_test.go
index 0ff010fd7..aa0edbf3a 100644
--- a/pkg/protocol/http1/resp/response_test.go
+++ b/pkg/protocol/http1/resp/response_test.go
@@ -687,6 +687,27 @@ func testSetResponseBodyStream(t *testing.T, body string) {
}
}
+func BenchmarkResponseWrite(b *testing.B) {
+ body := string(mock.CreateFixedBody(100500))
+ var resp protocol.Response
+ bodySize := len(body)
+ resp.SetBodyStream(bytes.NewBufferString(body), bodySize)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ var w bytes.Buffer
+ zw := netpoll.NewWriter(&w)
+ if err := Write(&resp, zw); err != nil {
+ b.Fatalf("unexpected error when writing response: %s. body=%q", err, body)
+ }
+ if err := zw.Flush(); err != nil {
+ b.Fatalf("unexpected error when flushing response: %s. body=%q", err, body)
+ }
+ resp.Reset()
+ }
+}
+
func testSetResponseBodyStreamChunked(t *testing.T, body string, trailer map[string]string) {
var resp protocol.Response
if resp.IsBodyStream() {
@@ -821,8 +842,29 @@ func TestResponseReadBodyStreamBadTrailer(t *testing.T) {
testResponseReadBodyStreamBadTrailer(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nproxy-connection: bar2\r\n\r\n")
}
+func BenchmarkReadBodyStream(b *testing.B) {
+ resp := &protocol.Response{}
+ zr := mock.NewZeroCopyReader("HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\ncontent-type: bar\r\n\r\n")
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ ReadBodyStream(resp, zr, 0, nil)
+ }
+}
+
func TestResponseString(t *testing.T) {
resp := protocol.Response{}
resp.Header.Set("Location", "foo\r\nSet-Cookie: SESSIONID=MaliciousValue\r\n")
assert.True(t, strings.Contains(GetHTTP1Response(&resp).String(), "Location: foo\r\nSet-Cookie: SESSIONID=MaliciousValue\r\n"))
}
+
+func BenchmarkGetHTTP1Response_String(b *testing.B) {
+ resp := protocol.Response{}
+ resp.Header.Set("Location", "foo\r\nSet-Cookie: SESSIONID=MaliciousValue\r\n")
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ _ = GetHTTP1Response(&resp).String()
+ }
+}
diff --git a/pkg/protocol/http1/resp/writer_test.go b/pkg/protocol/http1/resp/writer_test.go
index b57281ae3..eca003939 100644
--- a/pkg/protocol/http1/resp/writer_test.go
+++ b/pkg/protocol/http1/resp/writer_test.go
@@ -64,3 +64,17 @@ func TestNewChunkedBodyWriterNoData(t *testing.T) {
assert.True(t, strings.Contains(string(out), "Foo: Bar"))
assert.True(t, strings.Contains(string(out), "0"+string(bytestr.StrCRLF)+string(bytestr.StrCRLF)))
}
+
+func BenchmarkNewChunkedBodyWriter(b *testing.B) {
+ response := protocol.AcquireResponse()
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ mockConn := mock.NewConn("")
+ w := NewChunkedBodyWriter(response, mockConn)
+ w.Write([]byte("hello"))
+ w.Finalize()
+ w.Flush()
+ mockConn.WriterRecorder().ReadBinary(mockConn.WriterRecorder().WroteLen())
+ }
+}
diff --git a/pkg/protocol/multipart_test.go b/pkg/protocol/multipart_test.go
index 6b96ae86f..c66055ef8 100644
--- a/pkg/protocol/multipart_test.go
+++ b/pkg/protocol/multipart_test.go
@@ -101,6 +101,32 @@ Content-Type: application/json
}
}
+func BenchmarkWriteMultipartForm(b *testing.B) {
+ var w bytes.Buffer
+ s := strings.Replace(`--foo
+Content-Disposition: form-data; name="key"
+
+value
+--foo
+Content-Disposition: form-data; name="file"; filename="test.json"
+Content-Type: application/json
+
+{"foo": "bar"}
+--foo--
+`, "\n", "\r\n", -1)
+ mr := multipart.NewReader(strings.NewReader(s), "foo")
+ form, err := mr.ReadForm(1024)
+ assert.Nil(b, err)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ WriteMultipartForm(&w, form, s)
+ }
+ })
+}
+
func TestParseMultipartForm(t *testing.T) {
t.Parallel()
s := strings.Replace(`--foo
@@ -138,6 +164,25 @@ value
assert.NotNil(t, err)
}
+func BenchmarkParseMultipartForm(b *testing.B) {
+ s := strings.Replace(`--foo
+Content-Disposition: form-data; name="key"
+
+value
+--foo--
+`, "\n", "\r\n", -1)
+ req1 := Request{}
+ req1.SetMultipartFormBoundary("foo")
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ ParseMultipartForm(strings.NewReader(s), &req1, 1024, 1024)
+ }
+ })
+}
+
func TestWriteMultipartFormFile(t *testing.T) {
t.Parallel()
bodyBuffer := &bytes.Buffer{}
@@ -223,6 +268,30 @@ func TestWriteMultipartFormFile(t *testing.T) {
assert.Nil(t, WriteMultipartFormFile(w, "empty_test", "test.data", bytes.NewBuffer(nil)))
}
+func BenchmarkWriteMultipartFormFile(b *testing.B) {
+ bodyBuffer := &bytes.Buffer{}
+ w := multipart.NewWriter(bodyBuffer)
+
+ // read multipart.go to buf1
+ f1, err := os.Open("./multipart.go")
+ if err != nil {
+ b.Fatalf("open file %s error: %s", f1.Name(), err)
+ }
+ defer f1.Close()
+
+ multipartFile := File{
+ Name: f1.Name(),
+ ParamName: "multipartCode",
+ Reader: f1,
+ }
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ WriteMultipartFormFile(w, multipartFile.ParamName, f1.Name(), multipartFile.Reader)
+ }
+}
+
func TestMarshalMultipartForm(t *testing.T) {
s := strings.Replace(`--foo
Content-Disposition: form-data; name="key"
@@ -249,6 +318,31 @@ Content-Type: application/json
assert.NotNil(t, err)
}
+func BenchmarkMarshalMultipartForm(b *testing.B) {
+ s := strings.Replace(`--foo
+Content-Disposition: form-data; name="key"
+
+value
+--foo
+Content-Disposition: form-data; name="file"; filename="test.json"
+Content-Type: application/json
+
+{"foo": "bar"}
+--foo--
+`, "\n", "\r\n", -1)
+ mr := multipart.NewReader(strings.NewReader(s), "foo")
+ form, err := mr.ReadForm(1024)
+ if err != nil {
+ b.Fatalf("unexpected error: %s", err)
+ }
+
+ b.ResetTimer()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ MarshalMultipartForm(form, "foo")
+ }
+}
+
func TestAddFile(t *testing.T) {
t.Parallel()
bodyBuffer := &bytes.Buffer{}
diff --git a/pkg/protocol/request_test.go b/pkg/protocol/request_test.go
index fda96cd47..3a285aa4d 100644
--- a/pkg/protocol/request_test.go
+++ b/pkg/protocol/request_test.go
@@ -67,9 +67,18 @@ func (er errorReader) Read(p []byte) (int, error) {
func TestMultiForm(t *testing.T) {
var r Request
- // r.Header.Set()
_, err := r.MultipartForm()
- fmt.Println(err)
+ assert.NotNil(t, err)
+}
+
+func BenchmarkMultipartForm(b *testing.B) {
+ var r Request
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ r.MultipartForm()
+ }
}
func TestRequestBodyWriterWrite(t *testing.T) {
@@ -78,6 +87,16 @@ func TestRequestBodyWriterWrite(t *testing.T) {
assert.DeepEqual(t, "test", string(w.r.body.B))
}
+func Benchmark_RequestBodyWriterWrite(b *testing.B) {
+ w := requestBodyWriter{&Request{}}
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ w.Write([]byte("test"))
+ }
+}
+
func TestRequestScheme(t *testing.T) {
req := NewRequest("", "ptth://127.0.0.1:8080", nil)
assert.DeepEqual(t, "ptth", string(req.Scheme()))
@@ -117,6 +136,19 @@ func TestRequestSwapBody(t *testing.T) {
assert.DeepEqual(t, "testB", string(body))
}
+func BenchmarkSwapRequestBody(b *testing.B) {
+ reqA := &Request{}
+ reqB := &Request{}
+ reqB.SetBodyRaw([]byte("testB"))
+ reqA.SetBodyRaw([]byte("testA"))
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ SwapRequestBody(reqA, reqB)
+ }
+}
+
func TestRequestKnownSizeStreamMultipartFormWithFile(t *testing.T) {
t.Parallel()
diff --git a/pkg/protocol/response_test.go b/pkg/protocol/response_test.go
index 20a18ffce..d20609e80 100644
--- a/pkg/protocol/response_test.go
+++ b/pkg/protocol/response_test.go
@@ -92,6 +92,22 @@ func TestResponseBodyStreamMultipleBodyCalls(t *testing.T) {
}
}
+func BenchmarkResponseBodyStreamMultipleBodyCalls(b *testing.B) {
+ var r Response
+ s := "foobar baz abc"
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ r.SetBodyStream(bytes.NewBufferString(s), len(s))
+ body := r.Body()
+ if string(body) != s {
+ b.Fatalf("unexpected body %q. Expecting %q. iteration %d", body, s, i)
+ }
+ r.Reset()
+ }
+}
+
func TestResponseBodyWriteToPlain(t *testing.T) {
t.Parallel()
@@ -202,6 +218,20 @@ func TestResponseBodyGunzip(t *testing.T) {
assert.DeepEqual(t, zipData, src1)
}
+func BenchmarkResponse_BodyGunzip(b *testing.B) {
+ dst1 := []byte("")
+ src1 := []byte("hello")
+ res1 := compress.AppendGzipBytes(dst1, src1)
+ resp := Response{}
+ resp.SetBody(res1)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ resp.BodyGunzip()
+ }
+}
+
func TestResponseSwapResponseBody(t *testing.T) {
t.Parallel()
resp1 := Response{}
@@ -222,6 +252,28 @@ func TestResponseSwapResponseBody(t *testing.T) {
assert.DeepEqual(t, resp2.BodyStream(), bytes.NewBufferString(str1))
}
+func BenchmarkSwapResponseBody(b *testing.B) {
+ str1 := "resp1"
+ str2 := "resp2"
+
+ byteBuffer1 := &bytebufferpool.ByteBuffer{}
+ byteBuffer2 := &bytebufferpool.ByteBuffer{}
+ resp1 := Response{}
+ resp2 := Response{}
+
+ byteBuffer1.Set([]byte(str1))
+ resp1.ConstructBodyStream(byteBuffer1, bytes.NewBufferString(str1))
+
+ byteBuffer2.Set([]byte(str2))
+ resp2.ConstructBodyStream(byteBuffer2, bytes.NewBufferString(str2))
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ SwapResponseBody(&resp1, &resp2)
+ }
+}
+
func TestResponseAcquireResponse(t *testing.T) {
t.Parallel()
resp1 := AcquireResponse()
@@ -294,3 +346,20 @@ func TestResponse_HijackWriter(t *testing.T) {
resp.GetHijackWriter().Finalize()
assert.True(t, isFinal)
}
+
+func BenchmarkResponse_HijackWriter(b *testing.B) {
+ buf := new(bytes.Buffer)
+ isFinal := false
+ for i := 0; i < b.N; i++ {
+ resp := AcquireResponse()
+ resp.HijackWriter(&mock.ExtWriter{Buf: buf, IsFinal: &isFinal})
+ resp.AppendBody([]byte("hello"))
+ resp.GetHijackWriter().Flush()
+ resp.AppendBodyString(", world")
+ resp.GetHijackWriter().Flush()
+ resp.GetHijackWriter().Flush()
+ resp.GetHijackWriter().Finalize()
+ resp.Reset()
+ buf.Reset()
+ }
+}
diff --git a/pkg/protocol/trailer_test.go b/pkg/protocol/trailer_test.go
index ced1cc26f..0d5cd27db 100644
--- a/pkg/protocol/trailer_test.go
+++ b/pkg/protocol/trailer_test.go
@@ -17,6 +17,7 @@
package protocol
import (
+ "fmt"
"strings"
"testing"
@@ -36,6 +37,16 @@ func TestTrailerAdd(t *testing.T) {
assert.True(t, strings.Contains(string(tr.Header()), "Bar: value3"))
}
+func BenchmarkTrailer_Add(b *testing.B) {
+ var tr Trailer
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ tr.Add("bar", "value3")
+ }
+}
+
func TestHeaderTrailerSet(t *testing.T) {
h := &RequestHeader{}
@@ -89,6 +100,18 @@ func TestTrailerDel(t *testing.T) {
assert.True(t, strings.Contains(string(tr.Header()), "Bar: value3"))
}
+func BenchmarkTrailer_Del(b *testing.B) {
+ var tr Trailer
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ tr.Add("foo", "value3")
+ tr.Del("foo")
+ tr.Reset()
+ }
+}
+
func TestTrailerSet(t *testing.T) {
var tr Trailer
assert.Nil(t, tr.Set("foo", "value1"))
@@ -99,6 +122,16 @@ func TestTrailerSet(t *testing.T) {
assert.True(t, strings.Contains(string(tr.Header()), "Bar: value3"))
}
+func BenchmarkTrailer_Set(b *testing.B) {
+ var tr Trailer
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ tr.Set(fmt.Sprintf("foo%v", i), "value1")
+ }
+}
+
func TestTrailerGet(t *testing.T) {
var tr Trailer
assert.Nil(t, tr.Add("foo", "value1"))
@@ -118,6 +151,17 @@ func TestTrailerUpdateArgBytes(t *testing.T) {
assert.False(t, strings.Contains(string(tr.Header()), "Bar: value3"))
}
+func BenchmarkUpdateArgBytes(b *testing.B) {
+ var tr Trailer
+ tr.addArgBytes([]byte("Foo"), []byte("value0"), argsNoValue)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ tr.UpdateArgBytes([]byte("Foo"), []byte("value1"))
+ }
+}
+
func TestTrailerEmpty(t *testing.T) {
var tr Trailer
assert.DeepEqual(t, tr.Empty(), true)
diff --git a/pkg/protocol/uri_timing_test.go b/pkg/protocol/uri_timing_test.go
index 950117a2d..5abab2290 100644
--- a/pkg/protocol/uri_timing_test.go
+++ b/pkg/protocol/uri_timing_test.go
@@ -81,6 +81,8 @@ func BenchmarkURIFullURI(b *testing.B) {
func benchmarkURIParse(b *testing.B, host, uri string) {
strHost, strURI := []byte(host), []byte(uri)
+ b.ResetTimer()
+ b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
var u URI
for pb.Next() {
diff --git a/pkg/route/routes_timing_test.go b/pkg/route/routes_timing_test.go
index 4beb71d65..baabbd633 100644
--- a/pkg/route/routes_timing_test.go
+++ b/pkg/route/routes_timing_test.go
@@ -236,13 +236,8 @@ func BenchmarkTree_FindGithub(b *testing.B) {
// OAuth Authorizations
{"/authorizations"},
{"/authorizations/:id"},
- //{"/authorizations"},
- //{"/authorizations/clients/:client_id"},
- //{"/authorizations/:id"},
- //{"/authorizations/:id"},
{"/applications/:client_id/tokens/:access_token"},
{"/applications/:client_id/tokens"},
- //{"/applications/:client_id/tokens/:access_token"},
// Activity
{"/events"},
@@ -255,55 +250,33 @@ func BenchmarkTree_FindGithub(b *testing.B) {
{"/users/:user/events/public"},
{"/users/:user/events/orgs/:org"},
{"/feeds"},
- //{"/notifications"},
{"/repos/:owner/:repo/notifications"},
{"/notifications"},
- //{"/repos/:owner/:repo/notifications"},
{"/notifications/threads/:id"},
- //{"/notifications/threads/:id"},
{"/notifications/threads/:id/subscription"},
- //{"/notifications/threads/:id/subscription"},
- //{"/notifications/threads/:id/subscription"},
{"/repos/:owner/:repo/stargazers"},
{"/users/:user/starred"},
{"/user/starred"},
{"/user/starred/:owner/:repo"},
- //{"/user/starred/:owner/:repo"},
- //{"/user/starred/:owner/:repo"},
{"/repos/:owner/:repo/subscribers"},
{"/users/:user/subscriptions"},
{"/user/subscriptions"},
{"/repos/:owner/:repo/subscription"},
- //{"/repos/:owner/:repo/subscription"},
- //{"/repos/:owner/:repo/subscription"},
{"/user/subscriptions/:owner/:repo"},
- //{"PUT", "/user/subscriptions/:owner/:repo"},
- //{"DELETE", "/user/subscriptions/:owner/:repo"},
// Gists
{"/users/:user/gists"},
{"/gists"},
- //{"GET", "/gists/public"},
- //{"GET", "/gists/starred"},
{"/gists/:id"},
- //{"POST", "/gists"},
- //{"PATCH", "/gists/:id"},
{"/gists/:id/star"},
- //{"DELETE", "/gists/:id/star"},
- //{"GET", "/gists/:id/star"},
{"/gists/:id/forks"},
- //{"DELETE", "/gists/:id"},
// Git Data
{"/repos/:owner/:repo/git/blobs/:sha"},
{"/repos/:owner/:repo/git/blobs"},
{"/repos/:owner/:repo/git/commits/:sha"},
{"/repos/:owner/:repo/git/commits"},
- //{"GET", "/repos/:owner/:repo/git/refs/*ref"},
{"/repos/:owner/:repo/git/refs"},
- //{"POST", "/repos/:owner/:repo/git/refs"},
- //{"PATCH", "/repos/:owner/:repo/git/refs/*ref"},
- //{"DELETE", "/repos/:owner/:repo/git/refs/*ref"},
{"/repos/:owner/:repo/git/tags/:sha"},
{"/repos/:owner/:repo/git/tags"},
{"/repos/:owner/:repo/git/trees/:sha"},
@@ -314,35 +287,16 @@ func BenchmarkTree_FindGithub(b *testing.B) {
{"/orgs/:org/issues"},
{"/repos/:owner/:repo/issues"},
{"/repos/:owner/:repo/issues/:number"},
- //{"POST", "/repos/:owner/:repo/issues"},
- //{"PATCH", "/repos/:owner/:repo/issues/:number"},
{"/repos/:owner/:repo/assignees"},
{"/repos/:owner/:repo/assignees/:assignee"},
{"/repos/:owner/:repo/issues/:number/comments"},
- //{"GET", "/repos/:owner/:repo/issues/comments"},
- //{"GET", "/repos/:owner/:repo/issues/comments/:id"},
- //{"POST", "/repos/:owner/:repo/issues/:number/comments"},
- //{"PATCH", "/repos/:owner/:repo/issues/comments/:id"},
- //{"DELETE", "/repos/:owner/:repo/issues/comments/:id"},
{"/repos/:owner/:repo/issues/:number/events"},
- //{"GET", "/repos/:owner/:repo/issues/events"},
- //{"GET", "/repos/:owner/:repo/issues/events/:id"},
{"/repos/:owner/:repo/labels"},
{"/repos/:owner/:repo/labels/:name"},
- //{"POST", "/repos/:owner/:repo/labels"},
- //{"PATCH", "/repos/:owner/:repo/labels/:name"},
- //{"DELETE", "/repos/:owner/:repo/labels/:name"},
{"/repos/:owner/:repo/issues/:number/labels"},
- //{"POST", "/repos/:owner/:repo/issues/:number/labels"},
- //{"DELETE", "/repos/:owner/:repo/issues/:number/labels/:name"},
- //{"PUT", "/repos/:owner/:repo/issues/:number/labels"},
- //{"DELETE", "/repos/:owner/:repo/issues/:number/labels"},
{"/repos/:owner/:repo/milestones/:number/labels"},
{"/repos/:owner/:repo/milestones"},
{"/repos/:owner/:repo/milestones/:number"},
- //{"POST", "/repos/:owner/:repo/milestones"},
- //{"PATCH", "/repos/:owner/:repo/milestones/:number"},
- //{"DELETE", "/repos/:owner/:repo/milestones/:number"},
// Miscellaneous
{"/emojis"},
@@ -357,100 +311,55 @@ func BenchmarkTree_FindGithub(b *testing.B) {
{"/users/:user/orgs"},
{"/user/orgs"},
{"/orgs/:org"},
- //{"PATCH", "/orgs/:org"},
{"/orgs/:org/members"},
{"/orgs/:org/members/:user"},
- //{"DELETE", "/orgs/:org/members/:user"},
{"/orgs/:org/public_members"},
{"/orgs/:org/public_members/:user"},
- //{"PUT", "/orgs/:org/public_members/:user"},
- //{"DELETE", "/orgs/:org/public_members/:user"},
{"/orgs/:org/teams"},
{"/teams/:id"},
- //{"POST", "/orgs/:org/teams"},
- //{"PATCH", "/teams/:id"},
- //{"DELETE", "/teams/:id"},
{"/teams/:id/members"},
{"/teams/:id/members/:user"},
- //{"PUT", "/teams/:id/members/:user"},
- //{"DELETE", "/teams/:id/members/:user"},
{"/teams/:id/repos"},
{"/teams/:id/repos/:owner/:repo"},
- //{"PUT", "/teams/:id/repos/:owner/:repo"},
- //{"DELETE", "/teams/:id/repos/:owner/:repo"},
{"/user/teams"},
// Pull Requests
{"/repos/:owner/:repo/pulls"},
{"/repos/:owner/:repo/pulls/:number"},
- //{"POST", "/repos/:owner/:repo/pulls"},
- //{"PATCH", "/repos/:owner/:repo/pulls/:number"},
{"/repos/:owner/:repo/pulls/:number/commits"},
{"/repos/:owner/:repo/pulls/:number/files"},
{"/repos/:owner/:repo/pulls/:number/merge"},
- //{"PUT", "/repos/:owner/:repo/pulls/:number/merge"},
{"/repos/:owner/:repo/pulls/:number/comments"},
- //{"GET", "/repos/:owner/:repo/pulls/comments"},
- //{"GET", "/repos/:owner/:repo/pulls/comments/:number"},
- //{"PUT", "/repos/:owner/:repo/pulls/:number/comments"},
- //{"PATCH", "/repos/:owner/:repo/pulls/comments/:number"},
- //{"DELETE", "/repos/:owner/:repo/pulls/comments/:number"},
// Repositories
{"/user/repos"},
{"/users/:user/repos"},
{"/orgs/:org/repos"},
{"/repositories"},
- //{"POST", "/user/repos"},
- //{"POST", "/orgs/:org/repos"},
{"/repos/:owner/:repo"},
- //{"PATCH", "/repos/:owner/:repo"},
{"/repos/:owner/:repo/contributors"},
{"/repos/:owner/:repo/languages"},
{"/repos/:owner/:repo/teams"},
{"/repos/:owner/:repo/tags"},
{"/repos/:owner/:repo/branches"},
{"/repos/:owner/:repo/branches/:branch"},
- //{"DELETE", "/repos/:owner/:repo"},
{"/repos/:owner/:repo/collaborators"},
{"/repos/:owner/:repo/collaborators/:user"},
- //{"PUT", "/repos/:owner/:repo/collaborators/:user"},
- //{"DELETE", "/repos/:owner/:repo/collaborators/:user"},
{"/repos/:owner/:repo/comments"},
{"/repos/:owner/:repo/commits/:sha/comments"},
- //{"POST", "/repos/:owner/:repo/commits/:sha/comments"},
{"/repos/:owner/:repo/comments/:id"},
- //{"PATCH", "/repos/:owner/:repo/comments/:id"},
- //{"DELETE", "/repos/:owner/:repo/comments/:id"},
{"/repos/:owner/:repo/commits"},
{"/repos/:owner/:repo/commits/:sha"},
{"/repos/:owner/:repo/readme"},
- //{"GET", "/repos/:owner/:repo/contents/*path"},
- //{"PUT", "/repos/:owner/:repo/contents/*path"},
- //{"DELETE", "/repos/:owner/:repo/contents/*path"},
- //{"GET", "/repos/:owner/:repo/:archive_format/:ref"},
{"/repos/:owner/:repo/keys"},
{"/repos/:owner/:repo/keys/:id"},
- //{"POST", "/repos/:owner/:repo/keys"},
- //{"PATCH", "/repos/:owner/:repo/keys/:id"},
- //{"DELETE", "/repos/:owner/:repo/keys/:id"},
{"/repos/:owner/:repo/downloads"},
{"/repos/:owner/:repo/downloads/:id"},
- //{"DELETE", "/repos/:owner/:repo/downloads/:id"},
{"/repos/:owner/:repo/forks"},
- //{"POST", "/repos/:owner/:repo/forks"},
{"/repos/:owner/:repo/hooks"},
{"/repos/:owner/:repo/hooks/:id"},
- //{"POST", "/repos/:owner/:repo/hooks"},
- //{"PATCH", "/repos/:owner/:repo/hooks/:id"},
- //{"POST", "/repos/:owner/:repo/hooks/:id/tests"},
- //{"DELETE", "/repos/:owner/:repo/hooks/:id"},
- //{"POST", "/repos/:owner/:repo/merges"},
{"/repos/:owner/:repo/releases"},
{"/repos/:owner/:repo/releases/:id"},
- //{"POST", "/repos/:owner/:repo/releases"},
- //{"PATCH", "/repos/:owner/:repo/releases/:id"},
- //{"DELETE", "/repos/:owner/:repo/releases/:id"},
{"/repos/:owner/:repo/releases/:id/assets"},
{"/repos/:owner/:repo/stats/contributors"},
{"/repos/:owner/:repo/stats/commit_activity"},
@@ -458,7 +367,6 @@ func BenchmarkTree_FindGithub(b *testing.B) {
{"/repos/:owner/:repo/stats/participation"},
{"/repos/:owner/:repo/stats/punch_card"},
{"/repos/:owner/:repo/statuses/:ref"},
- //{"POST", "/repos/:owner/:repo/statuses/:ref"},
// Search
{"/search/repositories"},
@@ -473,25 +381,17 @@ func BenchmarkTree_FindGithub(b *testing.B) {
// Users
{"/users/:user"},
{"/user"},
- //{"PATCH", "/user"},
{"/users"},
{"/user/emails"},
- //{"POST", "/user/emails"},
- //{"DELETE", "/user/emails"},
{"/users/:user/followers"},
{"/user/followers"},
{"/users/:user/following"},
{"/user/following"},
{"/user/following/:user"},
{"/users/:user/following/:target_user"},
- //{"PUT", "/user/following/:user"},
- //{"DELETE", "/user/following/:user"},
{"/users/:user/keys"},
{"/user/keys"},
{"/user/keys/:id"},
- //{"POST", "/user/keys"},
- //{"PATCH", "/user/keys/:id"},
- //{"DELETE", "/user/keys/:id"},
}
for _, route := range static {
@@ -619,40 +519,62 @@ func BenchmarkTree_FindAnyFallback(b *testing.B) {
}
func BenchmarkRouteStatic(b *testing.B) {
- r := NewEngine(config.NewOptions(nil))
+ cfg := []config.Option{
+ {
+ F: func(o *config.Options) {
+ o.DisablePrintRoute = true
+ },
+ },
+ }
+ r := NewEngine(config.NewOptions(cfg))
r.GET("/hi/foo", func(c context.Context, ctx *app.RequestContext) {})
ctx := r.NewContext()
req := protocol.NewRequest("GET", "/hi/foo", nil)
- req.CopyTo(&ctx.Request)
b.ResetTimer()
for i := 0; i < b.N; i++ {
+ req.CopyTo(&ctx.Request)
r.ServeHTTP(context.Background(), ctx)
- // ctx.index = -1
+ ctx.Reset()
}
}
func BenchmarkRouteParam(b *testing.B) {
- r := NewEngine(config.NewOptions(nil))
+ cfg := []config.Option{
+ {
+ F: func(o *config.Options) {
+ o.DisablePrintRoute = true
+ },
+ },
+ }
+ r := NewEngine(config.NewOptions(cfg))
r.GET("/hi/:user", func(c context.Context, ctx *app.RequestContext) {})
ctx := r.NewContext()
req := protocol.NewRequest("GET", "/hi/foo", nil)
- req.CopyTo(&ctx.Request)
b.ResetTimer()
for i := 0; i < b.N; i++ {
+ req.CopyTo(&ctx.Request)
r.ServeHTTP(context.Background(), ctx)
- // ctx.index = -1
+ ctx.Reset()
}
}
func BenchmarkRouteAny(b *testing.B) {
- r := NewEngine(config.NewOptions(nil))
+ cfg := []config.Option{
+ {
+ F: func(o *config.Options) {
+ o.DisablePrintRoute = true
+ },
+ },
+ }
+ r := NewEngine(config.NewOptions(cfg))
r.GET("/hi/*user", func(c context.Context, ctx *app.RequestContext) {})
ctx := r.NewContext()
req := protocol.NewRequest("GET", "/hi/foo/dy", nil)
req.CopyTo(&ctx.Request)
b.ResetTimer()
for i := 0; i < b.N; i++ {
+ req.CopyTo(&ctx.Request)
r.ServeHTTP(context.Background(), ctx)
- // ctx.index = -1
+ ctx.Reset()
}
}