Skip to content

Use exact-size header arrays for output cache entries - #70195

Open
davidfowl wants to merge 1 commit into
mainfrom
perf/mw-outputcache-header-arrays
Open

davidfowl wants to merge 1 commit into
mainfrom
perf/mw-outputcache-header-arrays

Conversation

@davidfowl

@davidfowl davidfowl commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

Use exact-size header arrays for output cache entries

  • You've read the Contributor Guide and Code of Conduct.
  • You've included unit or integration tests for your change, where applicable.
  • You've included inline docs for your change, where applicable.
  • There's an open issue for the PR that you are making.

Allocate output cache header arrays at their exact size instead of renting them.

Description

OutputCacheEntryFormatter.Deserialize (every cache hit) and OutputCacheEntry.CopyHeadersFrom (every stored response) rent their header array from ArrayPool<(string, StringValues)>.Shared, but the arrays are never returned: OutputCacheEntry.Dispose is a no-op, and the entry keeps the array. The shared pool therefore never has an array to hand back, so each call allocates a new pool-sized array with at least 16 elements (16 B each) plus the pool lookup overhead.

This change allocates an array of exactly the needed length. The stored format, cache keys and the headers written to the response are unchanged. Because nothing is rented, no array can be shared between entries.

Benchmark: the existing EndToEndBenchmarks.ReadAsync, which deserializes a cached entry with 6 headers. Three interleaved A/B rounds:

PayloadLength Before (main) After
10 154-170 ns / 488 B 136-143 ns / 328 B
1000 150-154 ns / 488 B 137-139 ns / 328 B
65553 151-162 ns / 488 B 138-139 ns / 328 B
262161 149-158 ns / 488 B 137-142 ns / 328 B

The store path (CopyHeadersFrom) follows the same pattern: before, it rented a 16-element array for the 7 response headers; now it allocates a 7-element array.

Tests: Microsoft.AspNetCore.OutputCaching.Tests, 403/403. The existing formatter and middleware tests cover header round-tripping, including ignored headers.

🤖 Posted by ASP.NET Core Perf 2 (AI Agent)

The header arrays were rented from ArrayPool but never returned (the
entry's Dispose is a no-op), so every cache hit and every stored response
allocated a fresh pool-sized array (minimum 16 elements).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 31, 2026 16:24
@github-actions github-actions Bot added the area-middleware Includes: URL rewrite, redirect, response cache/compression, session, and other general middlewares label Oct 31, 2026

Copilot AI 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.

🟢 Approval recommended

There are no active review issues for the header-array allocation changes.

No open issues 🎉

What changed in this PR

This PR reduces output-cache header allocation overhead by replacing pooled arrays that were not returned with directly allocated arrays, while preserving serialization and response-header behavior.

Changes:

  • Allocate header arrays using the requested count during deserialization and response storage.
  • Remove pool-return handling and simplify header-memory assignment.
File Description
src/​Middleware/​OutputCaching/​src/​OutputCacheEntryFormatter.cs Allocates an exact-length array for deserialized headers and passes it directly to SetHeaders.
src/​Middleware/​OutputCaching/​src/​OutputCacheEntry.cs Replaces array rental with direct allocation while retaining ignored-header filtering and slicing.

Review effort: Balanced

This branch has not been deployed

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

Labels

area-middleware Includes: URL rewrite, redirect, response cache/compression, session, and other general middlewares

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants