Skip to content

perf: round-2 optimizations — 22 of 27 backlog items resolved - #28

Merged
aarambh-darshan merged 4 commits into
mainfrom
perf/round-2
Aug 24, 2026
Merged

aarambh-darshan merged 4 commits into
mainfrom
perf/round-2

Conversation

@aarambh-darshan

Copy link
Copy Markdown
Member

Summary

Second optimization pass over the post-audit codebase, working through the
AUDIT_FINDINGS.md Round-2 backlog (O1–O27) identified after the first
benchmark report. 22 of 27 items resolved; the rest are deferred with
reasons documented in the tracker.

Highlights

Core dispatch

  • ErasedHandler::call(&self) — dispatch fast path no longer allocates a
    boxed handler per request (O2)
  • Per-handler layer stacks folded once after with_state — nested routes
    with middleware no longer rebuild N boxed services per request (O3)
  • MethodRouter.layers behind an Arc — router clone is allocation-free (O8)
  • Path-param keys interned on a route's first match; 404/fallback sentinel
    labels become LazyLock statics (O5, O4)

Middleware

  • Compression negotiates from the borrowed header before the handler runs;
    absent/empty Accept-Encoding = zero work; allocation-free negotiator and
    fixed candidate lists (O12, O25)
  • Trace: latency formatting + span records skipped when the span is gated
    off; span fields borrow instead of allocating; static version table (O13)
  • Request-id builds one HeaderValue per request instead of two plus a string
    clone (O11); CORS derives its constant header values at construction —
    allocation-free preflights (O15)
  • Four synchronous-only services drop their per-request Box::pin (O16),
    including a two-variant future for the body-limit rejection path

Static / extractors / observe

  • Serve root canonicalized once instead of per request; duplicate stat
    removed — up to 3 fewer blocking-pool hops per file (O19, O20)
  • to_bytes_limited returns single-frame bodies zero-copy (O24)
  • Metrics layer: slim in-flight guard holding the gauge handle directly;
    stack-buffer status labels (O21)
  • Logging/OTel: route label skipped when the span is gated off; request-id
    HeaderValue built once; duplicate OTel set_attribute calls removed (O22, O23)
  • JSON/Form content-type fast paths; SSE normalization skips allocations when
    no CR present (O26)

Deferred (documented in tracker)

  • O1 — Handler-trait state-by-ref migration is breaking; the Arc'd-state
    clone at StateBound remains until then (Arc idiom documented at
    the call site)
  • O7/O10/O17/O18 — micro/inherent; reasons in AUDIT_FINDINGS.md

Verification

Full gates on rustc 1.98.0 (same as CI):

cargo clippy --workspace --all-features -- -D warnings   ✅ 0 errors
cargo fmt --all -- --check                               ✅
cargo test  --workspace --all-features                   ✅ 171 passed / 0 failed

Re-run the benchmark suite after merging to refresh BENCHMARK_REPORT.md.

aarambh-darshan and others added 4 commits August 24, 2026 10:27
O19/O20 (static): canonical serve root cached in OnceCell instead of
canonicalizing per request; select_file now receives the metadata that
handle already collected, removing a duplicate stat. Static requests
drop from up to five blocking-pool hops to ~3.

O11 (request-id): one HeaderValue built and cloned (refcount bump)
instead of cloning the id string plus two parse/validate/alloc cycles.

O12 (compression): encoding negotiation moved before inner.call using
the borrowed header; absent/empty Accept-Encoding short-circuits to
pass-through with zero allocations; preferred_encoding iterates a fixed
candidate list with no Vec churn; accept::negotiate rewritten
allocation-free (eq_ignore_ascii_case over the raw slice).

O13 (trace): when make_span gated the span, the response tail skipped
latency formatting and all record/event calls (previously an
unconditional String alloc per request). Span fields borrow
method/path and use a &'static str version table.

O3/O8 (router): MethodRouter.layers moved behind Arc (clone is now
allocation-free) and per-handler layer stacks are folded exactly once
after with_state binds state — nested routes with sub-router middleware
no longer rebuild N boxed services per request. Unbound generic routers
keep the legacy lazy path; layer/handler mutations invalidate the bake.

Co-Authored-By: Claude <noreply@anthropic.com>
…rrow

Round-2 follow-ups: O4, O5, O6, O9, O27.

O5 — path-param keys are interned on a route's first match (OnceLock on
the route entry) so later requests only refcount instead of copying each
name via Arc::from; values still copy only when decoding changed bytes.

O4 — 404/fallback sentinel labels (__fallback/__unmatched) are
LazyLock<Arc<str>> statics instead of a fresh heap allocation per
scanned request.

O6 — StripPrefixService no longer rebuilds a byte-identical Uri (alloc +
parse) when the prefix is "/" or does not apply.

O9 — ResponseBuilder::text_static for framework-generated bodies (404/
405): zero-copy body bytes and Content-Type from 'static data.

O27 — MethodRouter::call borrows the request method instead of cloning.

Co-Authored-By: Claude <noreply@anthropic.com>
Round-2 follow-ups: O16 + O9 completion.

- DefaultBodyLimitService: was Box::pin(inner.call(req)) — a double box
  of an already concrete future. Now type Future = S::Future, zero
  allocation.
- RequestBodyLimitService: happy path returns the inner future directly
  via a two-variant enum future (Accept = S::Future, Reject = a ready
  413). Only the rare over-limit rejection allocates.
- SetRequestHeaderService / SensitiveHeadersService: synchronous request
  mutation followed by pure delegation — same treatment.
- text_static used for the framework's own 404/405 bodies.

Co-Authored-By: Claude <noreply@anthropic.com>
O2 — ErasedHandler::call now takes &self (concrete handler cloned into
the returned future, H: Clone is already a supertrait). The dispatch
fast path calls the stored handler directly, removing one boxed
allocation per request on every route.

O13/O22/O23 — observe middlewares: latency formatting and span records
skipped entirely when the span was gated off; route label skipped when
irrelevant; request-id HeaderValue built once per request instead of
twice; OTel no longer sets the same attributes tracing-opentelemetry
already converts from recorded fields.

O24 — to_bytes_limited collects frame handles and returns a single
frame zero-copy; multi-frame bodies concatenate once into a
right-sized buffer.

O26 — JSON/Form content-type fast paths skip the full Mime parse for
exact constants; SSE data/comment normalization skips two allocations
when no CR is present.

O1 — investigated: StateBound's state clone feeds an argument the bound
handler ignores, but eliminating it requires the breaking
Handler-trait state-by-ref migration. Deferred with the Arc<AppState>
idiom documented at the call site.

Full gates on rustc 1.98: clippy --all-features -D warnings clean,
fmt clean, test --workspace --all-features 171 passed / 0 failed.

Co-Authored-By: Claude <noreply@anthropic.com>
@aarambh-darshan
aarambh-darshan merged commit 6d56f64 into main Aug 24, 2026
5 checks passed
@aarambh-darshan
aarambh-darshan deleted the perf/round-2 branch August 24, 2026 07:46
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.

1 participant