Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are Func<T> parameters in constructor slowing down my IoC resolving?

I'm trying to improve the performance of my IoC container. We are using Unity and SimpleInjector and we have a class with this constructor:

public AuditFacade(
    IIocContainer container, 
    Func<IAuditManager> auditManagerFactory,
    Func<ValidatorFactory> validatorCreatorFactory, 
    IUserContext userContext,
    Func<ITenantManager> tenantManagerFactory, 
    Func<IMonitoringComponent> monitoringComponentFactory)
    : base(container, auditManagerFactory, GlobalContext.CurrentTenant, 
          validatorCreatorFactory, userContext, tenantManagerFactory)
{
    _monitoringComponent = new Lazy<IMonitoringComponent>(monitoringComponentFactory);
}

I also have another class with this constructor:

public AuditTenantComponent(Func<IAuditTenantRepository> auditTenantRepository)
{
    _auditTenantRepository = new Lazy<IAuditTenantRepository>(auditTenantRepository);
}

I'm seeing that the second one gets resolved in 1 millisecond, most of the time, whereas the first one takes on average 50-60 milliseconds. I'm sure the reasoning for the slower one is because of the parameters, it has more parameters. But how can I improve the performance of this slower one? Is it the fact that we are using Func<T> as parameters? What can I change if it is causing the slowness?

like image 749
Ray Avatar asked Sep 30 '14 16:09

Ray


1 Answers

You can hook into Simple Injector's pipeline and add profiling, which allows you to spot which types are slow to create. Here's an extension method that you can use:

public struct ProfileData {
    public readonly ExpressionBuildingEventArgs Info;
    public readonly TimeSpan Elapsed;

    public ProfileData(ExpressionBuildingEventArgs info, TimeSpan elapsed) {
        this.Info = info;
        this.Elapsed = elapsed;
    }
}

static void EnableProfiling(Container container, List<ProfileData> profileLog) {
    container.ExpressionBuilding += (s, e) => {
        Func<Func<object>, object> profilingWrapper = creator => {
            var watch = Stopwatch.StartNew();
            var instance = creator.Invoke();
            profileLog.Add(new ProfileData(e, watch.Elapsed));
            return instance;
        };

        Func<object> instanceCreator = 
            Expression.Lambda<Func<object>>(e.Expression).Compile();

        e.Expression = Expression.Convert(
            Expression.Invoke(
                Expression.Constant(profilingWrapper),
                Expression.Constant(instanceCreator)),
            e.KnownImplementationType);
    };
}

And you can use this as follows:

var container = new Container();

// TODO: Your registrations here.

// Hook the profiler
List<ProfileData> profileLog = new List<ProfileData>(1000);

// Call this after all registrations.
EnableProfiling(container, profileLog);

// Trigger verification to allow everything to be precompiled.
container.Verify();

profileLog.Clear();

// Resolve a type:
container.GetInstance<AuditFacade>();

// Display resolve time in order of time.
var slowestFirst = profileLog.OrderByDescending(line => line.Elapsed);

foreach (var line in slowestFirst)
{
    Console.WriteLine(string.Format("{0} ms: {1}", 
        line.Info.KnownImplementationType.Name, 
        line.Elapsed.TotalMilliseconds);
}

Do note that the shown times include the time it takes to resolve the dependencies, but this will probably allow you pretty easily what type causes the delay.

There are two important thing I want to note about the given code here:

  1. This code will have severely negative impact on the performance of resolving object graphs, and
  2. The code is NOT thread-safe.

So don't use it in your production environment.

like image 154
Steven Avatar answered Oct 12 '22 13:10

Steven