Skip to content

Fix per-resolve closure allocation in resolve pipeline (#1493)#1494

Merged
tillig merged 6 commits into
developfrom
feature/issue-1493
Jul 9, 2026
Merged

Fix per-resolve closure allocation in resolve pipeline (#1493)#1494
tillig merged 6 commits into
developfrom
feature/issue-1493

Conversation

@tillig

@tillig tillig commented Jul 9, 2026

Copy link
Copy Markdown
Member

Fixes #1493.

Problem

@BrendanLally reported that per-resolve memory allocations increased in Autofac 9.2+ and traced it to ResolvePipelineBuilder.BuildPipeline. The diagnosis is correct.

The 9.2 "Consolidated metrics handling" refactor introduced a shared ExecuteWithDiagnostics(context, stage, Action action) helper, called like:

return context => ExecuteWithDiagnostics(context, stage, () =>
{
    context.PhaseReached = stagePhase;
    stage.Execute(context, next);
});

The inner () => { ... } lambda captures context — a per-invocation parameter — so a fresh closure is allocated for every middleware stage on every resolve. In 9.1 the equivalent lambda closed over only build-time state and was allocated once per pipeline build (pipelines are cached). BuildMetricsMiddlewareChain had the same defect.

Fix

Inline the diagnostics/metrics logic into each build-time lambda so they close only over build-time state (next, stage, stagePhase, stageName) and are allocated once per pipeline build. Both the standard and metrics-enabled chains are fixed. The two builders were extracted to static methods to keep BuildPipeline within the cognitive-complexity analyzer limit. Diagnostics and metrics behavior is unchanged.

Validation

Added ResolvePipelineAllocationBenchmark (mirroring the reported repro) and compared current source against the 9.1.0 NuGet package via the benchmark harness's baseline feature:

Scenario NoDependencies OneDependency
9.1.0 baseline 832 B 1336 B
9.2/9.3 (before fix) 1056 B (+224) 1784 B (+448)
After fix 832 B 1336 B

The 9.1.0 numbers match the reporter's exactly, and the fix fully restores them (the timing regression is gone too).

Tests

Added PipelineBuilderTests.InvokingBuiltPipelineDoesNotAllocatePerInvocation, asserting a built pipeline allocates zero bytes per invocation. Verified it fails on the pre-fix code (32 B/stage per resolve) and passes with the fix. Full core (870) and specification (498) suites pass; dotnet format is clean.

Since 9.2, the built middleware chain wrapped each stage in an
Action passed to a shared ExecuteWithDiagnostics helper. That inner
lambda captured the per-invocation ResolveRequestContext, so a fresh
closure was allocated for every middleware stage on every resolve,
regressing allocations vs. 9.1.

Inline the diagnostics/metrics logic into each build-time lambda so
they close only over build-time state (next, stage, stagePhase,
stageName) and are allocated once per pipeline build, restoring 9.1
allocation behavior. Both the standard and metrics-enabled chains are
fixed.

Add a ResolvePipelineAllocationBenchmark mirroring the reported repro,
and a PipelineBuilderTests regression test asserting a built pipeline
allocates zero bytes per invocation.
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.44444% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 78.24%. Comparing base (b7f65d6) to head (9b84ca8).

Files with missing lines Patch % Lines
.../Core/Resolving/Pipeline/ResolvePipelineBuilder.cs 94.44% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1494      +/-   ##
===========================================
+ Coverage    77.99%   78.24%   +0.24%     
===========================================
  Files          218      218              
  Lines         5945     5944       -1     
  Branches      1273     1273              
===========================================
+ Hits          4637     4651      +14     
+ Misses         768      753      -15     
  Partials       540      540              

☔ 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.

@tillig
tillig merged commit 40185be into develop Jul 9, 2026
14 checks passed
@tillig
tillig deleted the feature/issue-1493 branch July 9, 2026 17:05
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.

Increased Allocation on resolving services in autofac 9.2+

1 participant