Search code examples
autofac

Caching constructor selection instead of lamda conversion


We have a very large number of autofac resolutions that occur in our application. We've always had a very long time (about 50% of our web requests) of processing belonging to the autofac resolution stage.

It came to our attention how very important switching to Register Frequently-Used Components with Lambdas is from the guide, and our testing shows it could bring extreme gains for us. Convert from this

builder.RegisterType<Component>(); to builder.Register(c => new Component());

It seems the procedure to reflect to find the largest constructor, scan the properties, determine if autofac can resolve that, if not move on to the next smallest, etc. So specifying the constructor directly gives us massive improvements.

My question is can there be or should there be some sort of caching for the found constructor? Since the containers are immutable you can't add or subtract registrations so would require a different constructor later on.

I just wanted to check before we start working on switching over a lot of registrations to lambda, we have 1550 and will try to find the core ones.


Solution

  • Autofac does cache a lot of what it can, but even with container immutability, there's...

    • Registration sources: dynamic suppliers of registrations (that's how open generics get handled, for example)
    • Child lifetime scopes: the root container might not have the current HttpRequestMessage for the active WebAPI request, but you can add and override registrations in child scopes so the request lifetime does have it.
    • Parameters on resolve: you can pass parameters to a resolve operation that will get factored into that operation.

    Given stuff like that, caching is a little more complicated than "find the right constructor once, cache it, never look again."

    Lambdas will help, as you saw, so look into that.

    I'd also look at lifetime scopes for components. If constructor lookup is happening a lot, it means you're constructing a lot, which means you're allocating a lot, which is going to cost you other time. Can more components be singletons or per-lifetime-scope? Using an existing instance will be cheaper still.

    You also mentioned you have 1550 things registered. Do all of those actually get used and resolved? Don't register stuff that doesn't need to be. I've seen a lot of times where folks do assembly scanning and try to just register every type in every assembly in the whole app. I'm not saying you're doing this, but if you are, don't. Just register the stuff you need.

    Finally, think about how many constructors you have. If there's only one (recommended) it'll be faster than trying many different ones. You can also specify the constructor with UsingConstructor during registration to force a choice and bypass searching.