Skip to content

Commit ba9d619

Browse files
🚀 perf: Inline Request state wrappers (#3827)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Artur Melanchyk <13834276+arturmelanchyk@users.noreply.github.com>
1 parent 0c74c30 commit ba9d619

3 files changed

Lines changed: 71 additions & 13 deletions

File tree

client/hooks_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,7 @@ func Benchmark_Parser_Request_Body_File(b *testing.B) {
793793
func newBenchmarkRequest(formValues map[string]string, fileContents [][]byte) *Request {
794794
req := &Request{
795795
boundary: "FiberBenchmarkBoundary",
796-
formData: &FormData{Args: fasthttp.AcquireArgs()},
796+
formData: FormData{Args: fasthttp.AcquireArgs()},
797797
RawRequest: fasthttp.AcquireRequest(),
798798
files: make([]*File, len(fileContents)),
799799
}

client/request.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ type Request struct {
4747
ctx context.Context //nolint:containedctx // Context is needed to be stored in the request.
4848

4949
body any
50-
header *Header
51-
params *QueryParam
52-
cookies *Cookie
53-
path *PathParam
50+
header Header
51+
params QueryParam
52+
cookies Cookie
53+
path PathParam
5454

5555
client *Client
5656

57-
formData *FormData
57+
formData FormData
5858

5959
RawRequest *fasthttp.Request
6060
url string
@@ -324,7 +324,7 @@ func (r *Request) SetReferer(referer string) *Request {
324324
// Cookie returns the value of a named cookie.
325325
// If the cookie does not exist, an empty string is returned.
326326
func (r *Request) Cookie(key string) string {
327-
if val, ok := (*r.cookies)[key]; ok {
327+
if val, ok := r.cookies[key]; ok {
328328
return val
329329
}
330330
return ""
@@ -363,7 +363,7 @@ func (r *Request) DelCookies(key ...string) *Request {
363363
// PathParam returns the value of a named path parameter.
364364
// If the parameter does not exist, an empty string is returned.
365365
func (r *Request) PathParam(key string) string {
366-
if val, ok := (*r.path)[key]; ok {
366+
if val, ok := r.path[key]; ok {
367367
return val
368368
}
369369
return ""
@@ -967,12 +967,12 @@ func (f *File) Reset() {
967967
var requestPool = &sync.Pool{
968968
New: func() any {
969969
return &Request{
970-
header: &Header{RequestHeader: &fasthttp.RequestHeader{}},
971-
params: &QueryParam{Args: fasthttp.AcquireArgs()},
972-
cookies: &Cookie{},
973-
path: &PathParam{},
970+
header: Header{RequestHeader: &fasthttp.RequestHeader{}},
971+
params: QueryParam{Args: fasthttp.AcquireArgs()},
972+
cookies: Cookie{},
973+
path: PathParam{},
974974
boundary: boundary,
975-
formData: &FormData{Args: fasthttp.AcquireArgs()},
975+
formData: FormData{Args: fasthttp.AcquireArgs()},
976976
files: make([]*File, 0),
977977
RawRequest: fasthttp.AcquireRequest(),
978978
}

client/request_bench_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package client
2+
3+
import (
4+
"runtime"
5+
"runtime/metrics"
6+
"strconv"
7+
"testing"
8+
)
9+
10+
// BenchmarkRequestHeapScan measures how much heap memory the GC needs to scan
11+
// when a batch of requests is created and released.
12+
func BenchmarkRequestHeapScan(b *testing.B) {
13+
samples := []metrics.Sample{
14+
{Name: "/gc/scan/heap:bytes"},
15+
{Name: "/gc/scan/total:bytes"},
16+
}
17+
18+
b.ReportAllocs()
19+
b.StopTimer()
20+
b.ResetTimer()
21+
22+
const batchSize = 512
23+
var totalScanHeap, totalScanAll uint64
24+
for i := 0; i < b.N; i++ {
25+
reqs := make([]*Request, batchSize)
26+
// revive:disable-next-line:call-to-gc // Ensure consistent heap state before measuring scan metrics
27+
runtime.GC()
28+
metrics.Read(samples)
29+
startScanHeap := samples[0].Value.Uint64()
30+
startScanAll := samples[1].Value.Uint64()
31+
32+
b.StartTimer()
33+
for j := range reqs {
34+
req := AcquireRequest()
35+
req.SetHeader("X-Benchmark", "value")
36+
req.SetCookie("session", strconv.Itoa(j))
37+
req.SetPathParam("id", strconv.Itoa(j))
38+
req.SetParam("page", strconv.Itoa(j))
39+
reqs[j] = req
40+
}
41+
b.StopTimer()
42+
43+
// revive:disable-next-line:call-to-gc // Force GC to capture post-benchmark scan metrics
44+
runtime.GC()
45+
metrics.Read(samples)
46+
totalScanHeap += samples[0].Value.Uint64() - startScanHeap
47+
totalScanAll += samples[1].Value.Uint64() - startScanAll
48+
49+
for _, req := range reqs {
50+
ReleaseRequest(req)
51+
}
52+
}
53+
54+
if b.N > 0 {
55+
b.ReportMetric(float64(totalScanHeap)/float64(b.N), "scan-bytes-heap/op")
56+
b.ReportMetric(float64(totalScanAll)/float64(b.N), "scan-bytes-total/op")
57+
}
58+
}

0 commit comments

Comments
 (0)