Skip to content

feat: enhance performance#2135

Open
ReneWerner87 wants to merge 12 commits intovalyala:masterfrom
ReneWerner87:performance
Open

feat: enhance performance#2135
ReneWerner87 wants to merge 12 commits intovalyala:masterfrom
ReneWerner87:performance

Conversation

@ReneWerner87
Copy link
Copy Markdown
Contributor

@ReneWerner87 ReneWerner87 commented Feb 6, 2026

Performance Improvements Summary

This PR introduces benchmark-driven performance optimizations in core fasthttp hot paths and keeps only changes with statistically significant wins (benchstat, p < 0.05) and no allocation regressions.

1) Header parse hot-path: remove redundant key validation work

File: header.go

Changes

  • Added normalizeHeaderKeyValidated(...) for call sites where header-key validity has already been verified.
  • Replaced normalizeHeaderKey(...) with the validated variant in:
    • request header parsing
    • response header parsing
    • trailer parsing

Benchmark

BenchmarkRequestHeaderRead:

  • Before: 100.90 ns/op
  • After: 94.56 ns/op
  • Delta: -6.28% (p=0.026)
  • B/op and allocs/op remained 0

2) Cookie parsing: major reductions in time and allocations

File: cookie.go
Related helper reuse: bytesconv.go

Changes

  • Added fast RFC1123-GMT date parsing path for cookie Expires.
  • Switched cookie date parsing flow to:
    1. ParseHTTPDate(...) fast path
    2. legacy cookie date fallback (Mon, 02-Jan-2006 15:04:05 MST) for compatibility
  • Reworked cookie scanner in ParseBytes to a no-copy parsing flow (nextRaw) for intermediate token processing.
  • Added no-copy trim helper for cookie token parsing (trimCookieArgNoCopy), with copy only at final field assignment.

Benchmark

BenchmarkCookieParseFull:

  • Before (original): 291.6 ns/op, 163 B/op, 4 allocs/op
  • After: 85.15 ns/op, 0 B/op, 0 allocs/op
  • Net Delta: -70.8% (about -206 ns/op), -100% B/op, -100% allocs/op

3) Status line formatting: much faster and fewer allocations

File: status.go

Changes

  • Pre-grow destination buffer capacity before assembling the status line.
  • Added fast 3-digit status-code append path (avoids generic integer conversion for common codes).
  • Avoid repeated status text fallback evaluation in the formatter.

Benchmarks

  • BenchmarkStatusLine99:
    • Before: 46.99 ns/op, 120 B/op, 4 allocs/op
    • After: 19.49 ns/op, 48 B/op, 1 allocs/op
    • Delta: -58.52% (p=0.000)
  • BenchmarkStatusLine200:
    • Before: 26.38 ns/op, 56 B/op, 3 allocs/op
    • After: 11.29 ns/op, 24 B/op, 1 allocs/op
    • Delta: -57.21% (p=0.000)
  • BenchmarkStatusLine512:
    • Before: 47.03 ns/op, 120 B/op, 4 allocs/op
    • After: 18.91 ns/op, 48 B/op, 1 allocs/op
    • Delta: -59.79% (p=0.000)

@ReneWerner87 ReneWerner87 marked this pull request as ready for review February 7, 2026 06:51
Copilot AI review requested due to automatic review settings February 7, 2026 06:51
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces benchmark-driven micro-optimizations across fasthttp hot paths (request conversion, header normalization, cookie parsing, HTTP date parsing, and status line formatting) with the goal of reducing CPU time and allocations.

Changes:

  • Optimize status-line formatting by pre-growing buffers and adding a fast 3-digit status-code append path.
  • Reduce redundant header-key validation work by introducing normalizeHeaderKeyValidated(...) and using it in parsing hot paths.
  • Rework cookie parsing to avoid intermediate copies and add a fast RFC1123-GMT date parsing path used by ParseHTTPDate(...).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
status.go Pre-grows status-line buffer and adds a fast path for 3-digit status codes.
header.go Adds normalizeHeaderKeyValidated and uses it in request/response/trailer header parsing to avoid duplicate validation.
fasthttpadaptor/request.go Adds a fast-path origin-form request URI parse and improves request reuse hygiene (NoBody, RequestURI, TransferEncoding).
cookie.go Switches Set-Cookie parsing to a no-copy scanner and uses ParseHTTPDate fast path for Expires.
bytesconv.go Adds an RFC1123 “GMT” fast parser used by ParseHTTPDate.
bytesconv_timing_test.go Adds benchmarks for AppendHTTPDate.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@gaby
Copy link
Copy Markdown
Contributor

gaby commented Feb 7, 2026

@ReneWerner87 The security workflow probably needs to be updated. Since min go for fasthttp is not 1.20.x

@gaby
Copy link
Copy Markdown
Contributor

gaby commented Feb 7, 2026

It should be using 1.24.x, the version of gosec also needs to be updated.

@ReneWerner87
Copy link
Copy Markdown
Contributor Author

I'll look at your comments over the next few days.

@ReneWerner87
Copy link
Copy Markdown
Contributor Author

@erikdubbelboer Can you check again? I'm happy to move things around or remove/adjust them.

@ReneWerner87
Copy link
Copy Markdown
Contributor Author

I made this 219a75b change to make it more readable.

- `parseMonth3` (current packed vs original branch-based):
  - current: `40.82 ns/op`
  - original: `42.89 ns/op`
  - improvement: -4.83% (about -2.07 ns/op), `p=0.000`
  - allocations: unchanged (`0 B/op`, `0 allocs/op`)

- `isWeekday3` (current packed vs original branch-based):
  - current: `25.45 ns/op`
  - original: `26.56 ns/op`
  - improvement: -4.18% (about -1.11 ns/op), `p=0.000`
  - allocations: unchanged (`0 B/op`, `0 allocs/op`)

@ReneWerner87
Copy link
Copy Markdown
Contributor Author

@erikdubbelboer Can you check again? Should I adjust anything else?

Copy link
Copy Markdown
Contributor

@gaby gaby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small tweak

@ReneWerner87
Copy link
Copy Markdown
Contributor Author

@ReneWerner87
Copy link
Copy Markdown
Contributor Author

image

is not related to my code changes , just flaky

Copy link
Copy Markdown
Collaborator

@erikdubbelboer erikdubbelboer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nice! Only a couple more changes and then this can be merged.

b := []byte(s)

// Reference: time.Parse is what ParseHTTPDate falls back to.
stdTime, stdErr := time.Parse(time.RFC1123, s)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be http.TimeFormat.


// The fast path must never accept a string that time.Parse
// rejects, and when it accepts, the result must be identical.
fastTime, fastOK := parseRFC1123DateGMT(b)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't really make sense to call parseRFC1123DateGMT(b) if you're going to call ParseHTTPDate(b) right after anyways.

if t, ok := parseRFC1123DateGMT(date); ok {
return t, nil
}
return time.Parse(time.RFC1123, b2s(date))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change this to http.TimeFormat.

net/http also only accepts GMT as timezone.

b.ReportAllocs()
s := string(date)
for range b.N {
_, _ = time.Parse(time.RFC1123, s)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

http.TimeFormat

if err != nil {
exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", v)
if caseInsensitiveCompare(strCookieExpires, k) {
exptime, err := ParseHTTPDate(v)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cookies are supposed to be GMT as well, but they aren't always. net/http also supports other timezones here. If ParseHTTPDate is only going to support GMT, we need some fallbacks here. There is also the issue that other timezones should parse based on UTC, not based on the current time of the server (that's why it was using time.ParseInLocation).

I suggest to use this new function:

func parseCookieExpires(src []byte) (time.Time, error) {
	if t, ok := parseRFC1123DateGMT(src); ok {
		return t, nil
	}

	s := b2s(src)

	// UTC-anchored RFC1123 parsing behavior for non-GMT.
	t, err := time.ParseInLocation(time.RFC1123, s, time.UTC)
	if err == nil {
		return t, nil
	}

	// Legacy cookie date compatibility used by net/http.
	return time.Parse("Mon, 02-Jan-2006 15:04:05 MST", s)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants