Skip to content

⚡ feat: Add zero-allocation HTTP date, URL escaping, JSON string, IP parsing, and header key helpers - #238

Merged
ReneWerner87 merged 6 commits into
masterfrom
claude/gofiber-utils-performance-iqfedh
Jul 23, 2026
Merged

⚡ feat: Add zero-allocation HTTP date, URL escaping, JSON string, IP parsing, and header key helpers#238
ReneWerner87 merged 6 commits into
masterfrom
claude/gofiber-utils-performance-iqfedh

Conversation

@gaby

@gaby gaby commented Jul 23, 2026

Copy link
Copy Markdown
Member

Summary

Five new zero-allocation helper groups for Fiber's per-request paths. Each one is pinned byte-for-byte to its stdlib counterpart by unit tests, differential edge-case suites, and fuzz targets, and every new function is at 100% statement coverage.

HTTP dates (httpdate.go)

  • AppendHTTPDate / FormatHTTPDate write net/http.TimeFormat dates by overwriting a fixed 29-byte template instead of walking a layout string — 3.4x vs time.AppendFormat, 0 allocs.
  • ParseHTTPDate parses canonical RFC 9110 dates on a strict scalar fast path — 7.8x vs time.Parse — and delegates everything else (RFC 850, asctime, unusual casing, non-GMT zones, padding) to time.Parse with net/http.ParseTime semantics, including its errors.

URL escaping (url.go)

  • AppendQueryEscape / AppendPathEscape and the matching Unescape helpers reproduce net/url exactly (including url.EscapeError values) as single-pass append-style functions.
  • Unescaping jumps between escape sites with the vectorized scans (IndexAny2, bytes.IndexByte) and supports in-place decoding (dst = s[:0]) — 1.3x–5.5x, 0 allocs.

JSON string escaping (jsonstring.go)

  • AppendJSONString is byte-identical to encoding/json.Marshal of a string value (default HTML escaping, replacement, U+2028/U+2029 escapes) using a SWAR first-match scan over clean spans — 1.9x–6.9x, 0 allocs vs Marshal's reflection path.

IP parsing (ipparse.go)

  • ParseIPv4 / ParseIPv6 parse into netip.Addr, accepting exactly the per-family strings netip.ParseAddr accepts (pinned by fuzzing), reporting failure via bool instead of a constructed error, and taking []byte as well as string1.4x on IPv4, 1.25x–1.35x on IPv6.

Header key canonicalization (headerkey.go)

  • CanonicalHeaderKey mirrors net/http.CanonicalHeaderKey including its return-unchanged guard for invalid keys; already-canonical keys are validated in one table pass and returned as-is with zero allocations — 1.1x–1.4x. Adds internal/unsafeconv.Seq as the zero-copy inverse of unsafeconv.Bytes.

Deliberately left to the stdlib

Hex encode/decode, base64, and UUID stay with encoding/hex, encoding/base64, and google/uuid. A SWAR hex encoder/decoder was built and benchmarked during development but removed: the decoder measured slower than encoding/hex's table loop on amd64, and the maintainers prefer stdlib for these.

Testing

  • Fuzz targets for every helper group, cross-checking against time.Parse/net/http.ParseTime, net/url, encoding/json.Marshal, netip.ParseAddr, and http.CanonicalHeaderKey (~4M executions locally, zero divergence).
  • Differential edge-case suites (edgecase_test.go): per-position byte mutations and truncations, calendar boundaries (leap years, century rule, year 0000, clock limits), every byte value in each %XX escape position, malformed UTF-8 (overlong forms, surrogate halves, truncated sequences) swept across SWAR word-boundary offsets, and all 256 bytes at every position of representative header keys.
  • Coverage: 100% of statements in every new function, including internal/unsafeconv (new test file).
  • Full suite green with -race -shuffle=on; golangci-lint clean on all new files.

Benchmarks

Measured on linux/amd64 (Intel Xeon @ 2.80GHz); recorded in the README's amd64-labeled block alongside the simd numbers, joining the arm64 catalog on its next regeneration. Highlights (fiber vs default, ns/op):

Benchmark fiber stdlib
AppendHTTPDate 50.3 171.2
ParseHTTPDate 32.3 251.7
AppendQueryUnescape/plain-64B 20.7 136.8
AppendJSONString/clean-64B 64.1 271.8 (2 allocs)
ParseIPv4 20.0 28.7
CanonicalHeaderKey/custom-lower 73.1 102.6

🤖 Generated with Claude Code

https://claude.ai/code/session_01H8HVkHMPXuCHfz7W7fAaEj

claude added 4 commits July 23, 2026 03:56
Three new zero-allocation helper groups for Fiber's per-request paths,
each pinned to its stdlib counterpart by unit and fuzz tests:

- AppendHTTPDate/FormatHTTPDate write net/http.TimeFormat dates by
  overwriting a fixed template (3.4x vs time.AppendFormat), and
  ParseHTTPDate parses canonical RFC 9110 dates on a strict scalar fast
  path (7.8x vs time.Parse) while delegating legacy forms to time.Parse
  with net/http.ParseTime semantics.
- AppendQueryEscape/AppendPathEscape and the matching Unescape helpers
  reproduce net/url byte-for-byte (including EscapeError values) as
  single-pass append-style functions; unescaping skips between escape
  sites with IndexAny2/bytes.IndexByte and supports in-place decoding
  (1.3x-5.5x, no allocations).
- AppendHexEncode expands eight input bytes per SWAR word pair into
  lowercase hex (1.1x-1.4x vs encoding/hex.AppendEncode). A SWAR hex
  decoder measured slower than encoding/hex's table loop on amd64, so
  decoding deliberately stays with the stdlib.

Speedups are linux/amd64 measurements; the README records the new
benchmark rows in a separately labeled block like the simd package.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8HVkHMPXuCHfz7W7fAaEj
Hex encoding stays with the stdlib (hex.AppendEncode) alongside UUID and
base64; the URL unescape helpers keep their own internal hex-digit table,
which is not public API.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8HVkHMPXuCHfz7W7fAaEj
Three more zero-allocation helper groups for Fiber's per-request paths,
each pinned to its stdlib counterpart by unit and fuzz tests:

- AppendJSONString writes a double-quoted JSON string byte-identical to
  encoding/json.Marshal of the same value (default HTML escaping, \ufffd
  replacement, U+2028/U+2029 escapes) using a SWAR first-match scan over
  clean spans — 1.9x-6.9x with zero allocations versus Marshal's
  reflection path.
- ParseIPv4/ParseIPv6 parse into netip.Addr, accepting exactly the
  strings netip.ParseAddr accepts per family (1.4x on IPv4, 1.25x-1.35x
  on IPv6), reporting failure via bool instead of a constructed error and
  taking []byte as well as string input.
- CanonicalHeaderKey mirrors net/http.CanonicalHeaderKey byte for byte,
  validating already-canonical keys in one table pass and returning them
  as-is; rewritten keys cost one allocation (1.1x-1.4x). Adds an
  unsafeconv.Seq view helper as the zero-copy inverse of unsafeconv.Bytes.

Speedups are linux/amd64 measurements; README rows recorded in the
amd64-labeled block alongside the HTTP date and URL helpers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8HVkHMPXuCHfz7W7fAaEj
Bring every function added on this branch to 100% statement coverage and
pin the stdlib-parity contracts with enumerated input spaces instead of
hand-picked cases:

- ParseHTTPDate: per-position byte mutations and truncations of the
  canonical form, all twelve months and seven weekday names, and a
  calendar sweep (day 0/28-32/99 across leap, century, and year-0000
  boundaries; clock-field limits), all checked against the
  net/http.ParseTime reference.
- URL escaping: every byte value in each position of a %XX escape, long
  inputs that engage the vectorized scans with escapes at boundary
  offsets, late-error dst preservation, and the full byte alphabet
  through both escape modes.
- AppendJSONString: every special byte and multi-byte sequence swept
  across SWAR word-boundary offsets and string tails, plus malformed
  UTF-8 (overlong forms, surrogate halves, out-of-range and truncated
  sequences), all against encoding/json.Marshal.
- ParseIPv4/ParseIPv6: per-position mutations and truncations of seven
  representative addresses plus targeted invalid forms (covering the one
  previously unhit branch: a group followed by a non-separator byte),
  all against netip.ParseAddr.
- CanonicalHeaderKey: all 256 bytes at every position of eight key
  shapes against net/http.CanonicalHeaderKey, plus input-immutability.
- internal/unsafeconv: direct round-trip and aliasing tests for
  UnsafeString/UnsafeBytes/Bytes/Seq (package now at 100%).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8HVkHMPXuCHfz7W7fAaEj
@gaby
gaby requested a review from a team as a code owner July 23, 2026 06:44
@gaby
gaby requested review from ReneWerner87, efectn and sixcolors and removed request for a team July 23, 2026 06:44
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (master@02367e2). Learn more about missing BASE report.

Additional details and impacted files
@@            Coverage Diff            @@
##             master     #238   +/-   ##
=========================================
  Coverage          ?   92.71%           
=========================================
  Files             ?       33           
  Lines             ?     2140           
  Branches          ?        0           
=========================================
  Hits              ?     1984           
  Misses            ?      135           
  Partials          ?       21           
Flag Coverage Δ
unittests 92.71% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gaby gaby changed the title Add HTTP date, URL escaping, and hex encoding helpers Add zero-allocation HTTP date, URL escaping, JSON string, IP parsing, and header key helpers Jul 23, 2026
CI runs a newer golangci-lint than the Makefile pin and flagged four
issues: replace the twin-result table builder with a parameterized
single-table builder (confusing-results), replace the bool control
parameters in appendEscape/appendUnescape with an escapeMode enum
(flag-parameter), and store IPv6 groups via binary.BigEndian.PutUint16
instead of indexed writes gosec cannot prove in range (G602). No
behavior change; benchmarks unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8HVkHMPXuCHfz7W7fAaEj
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@gaby, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 39 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: accf5163-0b59-4930-8355-e8323dd580b4

📥 Commits

Reviewing files that changed from the base of the PR and between 02367e2 and 7f5ce13.

📒 Files selected for processing (15)
  • README.md
  • edgecase_test.go
  • fuzz_test.go
  • headerkey.go
  • headerkey_test.go
  • httpdate.go
  • httpdate_test.go
  • internal/unsafeconv/unsafeconv.go
  • internal/unsafeconv/unsafeconv_test.go
  • ipparse.go
  • ipparse_test.go
  • jsonstring.go
  • jsonstring_test.go
  • url.go
  • url_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/gofiber-utils-performance-iqfedh

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gaby gaby changed the title Add zero-allocation HTTP date, URL escaping, JSON string, IP parsing, and header key helpers ⚡ feat: Add zero-allocation HTTP date, URL escaping, JSON string, IP parsing, and header key helpers Jul 23, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: 7f5ce13 Previous: ab61971 Ratio
Benchmark_AddTrailingSlashBytes/empty (github.com/gofiber/utils/v2) 1.409 ns/op 0 B/op 0 allocs/op 0.5465 ns/op 0 B/op 0 allocs/op 2.58
Benchmark_AddTrailingSlashBytes/empty (github.com/gofiber/utils/v2) - ns/op 1.409 ns/op 0.5465 ns/op 2.58
Benchmark_AddTrailingSlashBytes/slash-only (github.com/gofiber/utils/v2) 1.763 ns/op 0 B/op 0 allocs/op 1.109 ns/op 0 B/op 0 allocs/op 1.59
Benchmark_AddTrailingSlashBytes/slash-only (github.com/gofiber/utils/v2) - ns/op 1.763 ns/op 1.109 ns/op 1.59
Benchmark_AddTrailingSlashBytes/short-with-slash (github.com/gofiber/utils/v2) 1.762 ns/op 0 B/op 0 allocs/op 1.096 ns/op 0 B/op 0 allocs/op 1.61
Benchmark_AddTrailingSlashBytes/short-with-slash (github.com/gofiber/utils/v2) - ns/op 1.762 ns/op 1.096 ns/op 1.61
Benchmark_AddTrailingSlashBytes/path-with-slash (github.com/gofiber/utils/v2) 1.765 ns/op 0 B/op 0 allocs/op 1.097 ns/op 0 B/op 0 allocs/op 1.61
Benchmark_AddTrailingSlashBytes/path-with-slash (github.com/gofiber/utils/v2) - ns/op 1.765 ns/op 1.097 ns/op 1.61
Benchmark_TrimLeftBytes/fiber (github.com/gofiber/utils/v2) 3.24 ns/op 0 B/op 0 allocs/op 1.819 ns/op 0 B/op 0 allocs/op 1.78
Benchmark_TrimLeftBytes/fiber (github.com/gofiber/utils/v2) - ns/op 3.24 ns/op 1.819 ns/op 1.78
Benchmark_TrimSpace/fiber/empty (github.com/gofiber/utils/v2) 0.7107 ns/op 0 B/op 0 allocs/op 0.276 ns/op 0 B/op 0 allocs/op 2.57
Benchmark_TrimSpace/fiber/empty (github.com/gofiber/utils/v2) - ns/op 0.7107 ns/op 0.276 ns/op 2.57
Benchmark_TrimSpace/fiber/spaces (github.com/gofiber/utils/v2) 4.227 ns/op 709.76 MB/s 0 B/op 0 allocs/op 2.271 ns/op 1320.91 MB/s 0 B/op 0 allocs/op 1.86
Benchmark_TrimSpace/fiber/spaces (github.com/gofiber/utils/v2) - ns/op 4.227 ns/op 2.271 ns/op 1.86
Benchmark_TrimSpaceBytes/fiber/ascii-word (github.com/gofiber/utils/v2) 4.581 ns/op 1964.65 MB/s 0 B/op 0 allocs/op 2.74 ns/op 3284.62 MB/s 0 B/op 0 allocs/op 1.67
Benchmark_TrimSpaceBytes/fiber/ascii-word (github.com/gofiber/utils/v2) - ns/op 4.581 ns/op 2.74 ns/op 1.67
Benchmark_TrimSpaceBytes/fiber/content-type (github.com/gofiber/utils/v2) 4.204 ns/op 4756.84 MB/s 0 B/op 0 allocs/op 2.738 ns/op 7305.47 MB/s 0 B/op 0 allocs/op 1.54
Benchmark_TrimSpaceBytes/fiber/content-type (github.com/gofiber/utils/v2) - ns/op 4.204 ns/op 2.738 ns/op 1.54
Benchmark_TrimSpaceBytes/fiber/query-params (github.com/gofiber/utils/v2) 4.229 ns/op 4729.21 MB/s 0 B/op 0 allocs/op 2.737 ns/op 7307.90 MB/s 0 B/op 0 allocs/op 1.55
Benchmark_TrimSpaceBytes/fiber/query-params (github.com/gofiber/utils/v2) - ns/op 4.229 ns/op 2.737 ns/op 1.55
Benchmark_TrimSpaceBytes/fiber/mixed-whitespace (github.com/gofiber/utils/v2) 5.646 ns/op 3011.16 MB/s 0 B/op 0 allocs/op 3.598 ns/op 4725.03 MB/s 0 B/op 0 allocs/op 1.57
Benchmark_TrimSpaceBytes/fiber/mixed-whitespace (github.com/gofiber/utils/v2) - ns/op 5.646 ns/op 3.598 ns/op 1.57
Benchmark_ToString/uint16 (github.com/gofiber/utils/v2) 4.602 ns/op 0 B/op 0 allocs/op 2.527 ns/op 0 B/op 0 allocs/op 1.82
Benchmark_ToString/uint16 (github.com/gofiber/utils/v2) - ns/op 4.602 ns/op 2.527 ns/op 1.82
Benchmark_ToString/uint32 (github.com/gofiber/utils/v2) 4.225 ns/op 0 B/op 0 allocs/op 2.557 ns/op 0 B/op 0 allocs/op 1.65
Benchmark_ToString/uint32 (github.com/gofiber/utils/v2) - ns/op 4.225 ns/op 2.557 ns/op 1.65
Benchmark_ToString/utils.MyStringer (github.com/gofiber/utils/v2) 5.989 ns/op 0 B/op 0 allocs/op 3.826 ns/op 0 B/op 0 allocs/op 1.57
Benchmark_ToString/utils.MyStringer (github.com/gofiber/utils/v2) - ns/op 5.989 ns/op 3.826 ns/op 1.57
Benchmark_ToString_concurrency/uint16 (github.com/gofiber/utils/v2) 2.131 ns/op 0 B/op 0 allocs/op 1.24 ns/op 0 B/op 0 allocs/op 1.72
Benchmark_ToString_concurrency/uint16 (github.com/gofiber/utils/v2) - ns/op 2.131 ns/op 1.24 ns/op 1.72
Benchmark_ToString_concurrency/uint32 (github.com/gofiber/utils/v2) 2.137 ns/op 0 B/op 0 allocs/op 1.232 ns/op 0 B/op 0 allocs/op 1.73
Benchmark_ToString_concurrency/uint32 (github.com/gofiber/utils/v2) - ns/op 2.137 ns/op 1.232 ns/op 1.73
Benchmark_FormatUint/small/fiber (github.com/gofiber/utils/v2) 2.46 ns/op 0 B/op 0 allocs/op 1.637 ns/op 0 B/op 0 allocs/op 1.50
Benchmark_FormatUint/small/fiber (github.com/gofiber/utils/v2) - ns/op 2.46 ns/op 1.637 ns/op 1.50
Benchmark_ParseInt16/fiber (github.com/gofiber/utils/v2) 8.806 ns/op 0 B/op 0 allocs/op 5.784 ns/op 0 B/op 0 allocs/op 1.52
Benchmark_ParseInt16/fiber (github.com/gofiber/utils/v2) - ns/op 8.806 ns/op 5.784 ns/op 1.52
Benchmark_ParseInt16/fiber_bytes (github.com/gofiber/utils/v2) 8.993 ns/op 0 B/op 0 allocs/op 5.965 ns/op 0 B/op 0 allocs/op 1.51
Benchmark_ParseInt16/fiber_bytes (github.com/gofiber/utils/v2) - ns/op 8.993 ns/op 5.965 ns/op 1.51
Benchmark_IndexAny2/64B/swar (github.com/gofiber/utils/v2) - MB/s 9078.25 MB/s 4985.85 MB/s 1.82
Benchmark_IndexAny2/512B/swar (github.com/gofiber/utils/v2) - MB/s 29045.81 MB/s 5731.49 MB/s 5.07
Benchmark_IndexAny3/64B/swar (github.com/gofiber/utils/v2) - MB/s 7874.52 MB/s 3971.05 MB/s 1.98
Benchmark_IndexAny3/512B/swar (github.com/gofiber/utils/v2) - MB/s 26904.54 MB/s 4512.22 MB/s 5.96
Benchmark_IsASCII/32B/swar (github.com/gofiber/utils/v2) 6.956 ns/op 4600.33 MB/s 0 B/op 0 allocs/op 3.597 ns/op 8895.25 MB/s 0 B/op 0 allocs/op 1.93
Benchmark_IsASCII/32B/swar (github.com/gofiber/utils/v2) - ns/op 6.956 ns/op 3.597 ns/op 1.93
Benchmark_AddTrailingSlashString/empty (github.com/gofiber/utils/v2) 0.704 ns/op 0 B/op 0 allocs/op 0.2736 ns/op 0 B/op 0 allocs/op 2.57
Benchmark_AddTrailingSlashString/empty (github.com/gofiber/utils/v2) - ns/op 0.704 ns/op 0.2736 ns/op 2.57
Benchmark_AddTrailingSlashString/slash-only (github.com/gofiber/utils/v2) 0.7063 ns/op 0 B/op 0 allocs/op 0.4095 ns/op 0 B/op 0 allocs/op 1.72
Benchmark_AddTrailingSlashString/slash-only (github.com/gofiber/utils/v2) - ns/op 0.7063 ns/op 0.4095 ns/op 1.72
Benchmark_AddTrailingSlashString/short-with-slash (github.com/gofiber/utils/v2) 0.7037 ns/op 0 B/op 0 allocs/op 0.4095 ns/op 0 B/op 0 allocs/op 1.72
Benchmark_AddTrailingSlashString/short-with-slash (github.com/gofiber/utils/v2) - ns/op 0.7037 ns/op 0.4095 ns/op 1.72
Benchmark_AddTrailingSlashString/path-with-slash (github.com/gofiber/utils/v2) 0.7043 ns/op 0 B/op 0 allocs/op 0.4101 ns/op 0 B/op 0 allocs/op 1.72
Benchmark_AddTrailingSlashString/path-with-slash (github.com/gofiber/utils/v2) - ns/op 0.7043 ns/op 0.4101 ns/op 1.72
Benchmark_ToLower/large-lower/default (github.com/gofiber/utils/v2/strings) 63.46 ns/op 0 B/op 0 allocs/op 41.46 ns/op 0 B/op 0 allocs/op 1.53
Benchmark_ToLower/large-lower/default (github.com/gofiber/utils/v2/strings) - ns/op 63.46 ns/op 41.46 ns/op 1.53

This comment was automatically generated by workflow using github-action-benchmark.

Code review found two contract gaps: AppendJSONString omitted the
dst-must-not-alias-s rule its sibling Append*Escape helpers document
(the output is longer than the input, so an aliased dst overwrites bytes
before they are read), and the unescape helpers' in-place mode did not
state that a failed decode has already overwritten the input prefix with
decoded bytes. Documentation only; no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8HVkHMPXuCHfz7W7fAaEj

gaby commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

The benchmark / benchmark failure is a false alarm — none of the flagged benchmarks are code paths this PR modifies, and no action in this PR can fix it:

  • The stored baseline predates the simd merge. The comparison is against 6488cc3 (master before ⚡ feat: Add SIMD-accelerated search package #237). The IndexAny2/*/swar, IndexAny3/*/swar, and IsASCII/512B/swar rows are all MB/s (higher is better) — the "regressions" are actually the 1.6–8x throughput improvements from ⚡ feat: Add SIMD-accelerated search package #237's AVX2 dispatch, first measured against the pre-simd baseline here. github-action-benchmark flags any >1.5x value change without accounting for bigger-is-better units.
  • The two ns/op rows are sub-clock-cycle noise. TrimSpace/fiber/empty and AddTrailingSlashString/empty moved 0.31 → 0.62 ns/op on empty-input fast paths untouched since the baseline — that's a single CPU cycle of runner-to-runner variance, not a regression.

The alert will likely recur on future pushes until a master benchmark run refreshes the baseline past #237.


Generated by Claude Code

@ReneWerner87

Copy link
Copy Markdown
Member

gaby commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

Indeed, same shape as your fasthttp change: a strict 29-byte RFC1123-GMT fast path with fixed separator offsets, packed 3-byte weekday/month matching, and a stdlib fallback. A few deltas in this version, for comparison:

  • The fallback mirrors net/http.ParseTime exactly — textproto-style whitespace trim, then RFC1123 → RFC850 → asctime — so the obsolete formats keep working and errors match the stdlib. That parity is pinned by fuzzing plus positional-mutation and calendar-boundary suites against a ParseTime reference.
  • Calendar validation happens up front (days-in-month + leap rule) instead of construct-and-compare-back, so the fast path never builds a time.Time it will discard. Exact-case names take the fast path; unusual casing rides the fallback, where time.Parse folds case like your |0x20 matcher does.
  • It's generic over ~string | ~[]byte, so fasthttp-style []byte callers skip the conversion, and the same file adds the write side (AppendHTTPDate/FormatHTTPDate, byte-identical to Format(http.TimeFormat)).

If it's useful upstream, fasthttp could converge on this helper once it lands here.


Generated by Claude Code

gaby commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

The benchmark alert fired again on 7f5ce13 (a doc-comment-only commit) with a different set of flagged rows — this run's evidence makes the noise diagnosis clearer:

  • Every flagged row (AddTrailingSlashBytes, TrimLeftBytes, TrimSpace*, ToString/uint16) is a 0.3–5 ns micro-benchmark showing a uniform ~1.5–2.6x slowdown, including code paths this PR doesn't touch at all — the signature of a slower runner, not a regression.
  • The comparison baseline also changed between the two runs (6488cc3ab61971), so consecutive runs of this same PR aren't even measured against the same reference.

Nothing in this PR can turn this check green deterministically; a threshold above 1.5 (or ignoring sub-ns rows) in the shared benchmark workflow would be the structural fix. I won't re-post on further recurrences of this alert.


Generated by Claude Code

@ReneWerner87
ReneWerner87 merged commit 5c51198 into master Jul 23, 2026
17 of 18 checks passed
@ReneWerner87
ReneWerner87 deleted the claude/gofiber-utils-performance-iqfedh branch July 23, 2026 10:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants