perf: reduce runtime Refit memory allocations#2263
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2263 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 181 183 +2
Lines 9723 9739 +16
Branches 1861 1865 +4
=========================================
+ Hits 9723 9739 +16 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Cut allocations across the runtime request/response/serialization hot paths, behaviour-identical (1161 tests green), guided by GcVerbose micro-benchmarks. - Reflection form mapping: cache per-type attribute metadata; FormValueMultimap.Create 2736 -> 776 B. - Path building: escape catch-all sections inline from the source span under the default formatter; RoundTripEscapePath 1416 -> 72 B. - Parameter formatting: render values without a composite string.Format and skip the query-attribute array when none is defined; string values 328 -> 0 B, and the int/date/guid/enum paths drop sharply. - Enum handling: build the camelCase name in a single allocation, build the serialized-name map without a per-field iterator, and scan the enum's fields once for all three converter maps; ToCamelCase 104 -> 56 B, converter construction 1624 -> 1178 B. - Add a Refit.Benchmarks GcVerbose micro-benchmark suite (97 benchmarks across 10 components) covering the hot path, plus guard tests pinning each optimised method's functional contract, and widen the relevant private members to internal (fields stay private) so the benchmarks can reach them. No public API change; behaviour and serialised output are unchanged.
glennawatson
force-pushed
the
perf/runtime-allocations
branch
from
July 20, 2026 05:44
9750cd2 to
7f7b971
Compare
|
ChrisPulman
approved these changes
Jul 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What kind of change does this PR introduce?
Performance / refactor of the runtime Refit library. No behavioural change and no public API change.
What is the new behavior?
Reduced memory allocations across the runtime request/response/serialization hot paths, guided by a new GcVerbose micro-benchmark suite and verified behaviour-identical:
FormValueMultimap.Create2736 -> 776 B).RoundTripEscapePath1416 -> 72 B).string.Formatand skips the query-attribute array when none is defined (string values 328 -> 0 B; the int/date/guid/enum paths drop sharply).ToCamelCase104 -> 56 B; converter construction 1624 -> 1178 B).Refit.BenchmarksGcVerbose micro-benchmark suite (97 benchmarks across 10 components) covering the hot path, plus guard tests pinning each optimised method's functional contract; the relevant private members are widened to internal (fields stay private) so the benchmarks can reach them.What is the current behavior?
The runtime paths above materialised more short-lived allocations than necessary - composite-format boxing, per-section substrings, repeated reflection reads, and intermediate strings in the enum converter build.
What might this PR break?
None. Behaviour and serialised output are unchanged, verified by the full
Refit.Testssuite (1161 passing) plus new guard tests pinning each optimised method's contract. No public API changes; the widened members areinternal.Checklist
mainbranchAdditional information
Allocation measured with BenchmarkDotNet (
Allocated, deterministic) plus EventPipe GcVerbose traces. Selected per-method reductions (bytes/op, net10):A reflection census confirmed the runtime library is source-gen-first (the generated System.Text.Json path is reflection-free); the residual reflection is confined to dynamic by-Type entry points and deliberate fallbacks and is cached to zero allocation per request. Remaining allocations were each measured and left only where irreducible (for example a value-type box forced by the object-returning reflection API on the fallback path, or framework HttpHeaders value storage).