Skip to content

Increased Allocation on resolving services in autofac 9.2+ #1493

Description

@BrendanLally

Describe the Bug

We were evaluating an autofac upgrade and found that our memory allocations shifted on service creation, I've been able to trace this to ResolvePipelineBuilder.BuildPipeline, since version 9.2 this uses a lamda that captures the resolve request context. versions 9.1 and before did not do this.

Steps to Reproduce

run this as a new console project linking either autofac <=9.1 or autofac >=9.2:

using Autofac;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

BenchmarkRunner.Run<ResolveAllocation>();

public interface IService { }
public sealed class Service : IService { }

public interface IDependency { }
public sealed class Dependency : IDependency { }
public interface IConsumer { }
public sealed class Consumer(IDependency dependency) : IConsumer { }

[MemoryDiagnoser]
public class ResolveAllocation
{
    private IContainer _container = null!;

    [GlobalSetup]
    public void Setup()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<Service>().As<IService>();
        builder.RegisterType<Dependency>().As<IDependency>();
        builder.RegisterType<Consumer>().As<IConsumer>();
        _container = builder.Build();
    }

    [Benchmark(Baseline = true)]
    public IService NoDependencies() => _container.Resolve<IService>();

    [Benchmark]
    public IConsumer OneDependency() => _container.Resolve<IConsumer>();
}

Expected Behavior

on autofac 9.1 I get:

Method Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
NoDependencies 151.3 ns 2.57 ns 4.95 ns 1.00 0.04 0.0663 832 B 1.00
OneDependency 350.8 ns 6.82 ns 7.30 ns 2.32 0.09 0.1063 1336 B 1.61

but on autofac 9.2 I get:

Method Mean Error StdDev Median Ratio RatioSD Gen0 Allocated Alloc Ratio
NoDependencies 213.8 ns 8.86 ns 26.13 ns 204.3 ns 1.01 0.17 0.0839 1.03 KB 1.00
OneDependency 431.6 ns 8.60 ns 13.40 ns 429.2 ns 2.05 0.25 0.1421 1.74 KB 1.69

I expect the allocated number to be the same between versions.

Dependency Versions

9.1 -> 9.2 upgrade.

Additional Info

BuildMetricsMiddlewareChain is probably also affected in the same way.

The following is a fairly minimal patch that I was able to apply to 9.2/3 to recover the 9.1 allocation numbers, I didn't try to regression test against the rest of autofac, nor did I try to address the metrics chain:

diff --git a/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs b/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs
--- a/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs
+++ b/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs
@@ -174,13 +174,36 @@ private static ResolvePipeline BuildPipeline(MiddlewareDeclaration? lastDecl)
         Action<ResolveRequestContext> BuildStandardMiddlewareChain(Action<ResolveRequestContext> next, IResolveMiddleware stage)
         {
             var stagePhase = stage.Phase;

-            // Hot path when execution metrics are disabled.
-            return context => ExecuteWithDiagnostics(context, stage, () =>
+            return context =>
             {
-                context.PhaseReached = stagePhase;
-                stage.Execute(context, next);
-            });
+                if (!context.DiagnosticSource.IsEnabled())
+                {
+                    context.PhaseReached = stagePhase;
+                    stage.Execute(context, next);
+                    return;
+                }
+
+                context.DiagnosticSource.MiddlewareStart(context, stage);
+                var succeeded = false;
+                try
+                {
+                    context.PhaseReached = stagePhase;
+                    stage.Execute(context, next);
+                    succeeded = true;
+                }
+                finally
+                {
+                    if (succeeded)
+                    {
+                        context.DiagnosticSource.MiddlewareSuccess(context, stage);
+                    }
+                    else
+                    {
+                        context.DiagnosticSource.MiddlewareFailure(context, stage);
+                    }
+                }
+            };
         }

         static void ExecuteWithDiagnostics(ResolveRequestContext context, IResolveMiddleware stage, Action action)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions