Slow build of container

546 views
Skip to first unread message

mgu

unread,
Feb 26, 2014, 6:46:04 AM2/26/14
to aut...@googlegroups.com
Hi

We have several solutions, sharing projects, each solution consisting of about 6000 classes (some private, some shared).

The assembly scanning and registering is fine, but the Build method takes up a lot of time.

We then did a profiling of autofac, and found that this method this the culprit, especially the ToArray() part, which then goes further down to ServiceRegistrationInfo, which then checks for the IsInitialized field.



The profiling is attached (don't complain about the Update, the Build has the same problem, and I did not write that :))

void UpdateInitialisedAdapters(IComponentRegistration registration)
{

var adapterServices = _serviceInfo
.Where(si => si.Value.ShouldRecalculateAdaptersOn(registration))
.Select(si => si.Key)
.ToArray();
if (adapterServices.Length == 0)
return;
Debug.WriteLine(String.Format(CultureInfo.InvariantCulture,
"[Autofac] Component '{0}' provides services that have already been adapted. Consider refactoring to ContainerBuilder.Build() rather than Update().",
registration));
var adaptationSandbox = new AdaptationSandbox(
_dynamicRegistrationSources.Where(rs => rs.IsAdapterForIndividualComponents),
registration,
adapterServices);
var adapters = adaptationSandbox.GetAdapters();
foreach (var adapter in adapters)
AddRegistration(adapter, true);
}

For fun, we removed this method call (we just returned as the first line), and all our tests passed, and the program functions as excepted.


Now:
We have a lot of tests, and a lot of them are written by creating their own servicelocator, so this adds up to a lot if time (about 2 seconds per test), we can shave off over 30 minutes of our testing.

In addtion the startup of the of the application is improved by 2 seconds, which is quite a lot.

The isolated improvement is from 2 seconds to 0,1 seconds in the containerbuild.

So my question is:
What scenarios is this needed for? And could we optimize this call to either be fast or parameter driven? These seconds and minutes means a lot to our project.


Travis Illig

unread,
Feb 26, 2014, 10:31:25 AM2/26/14
to aut...@googlegroups.com
Are you asking what the "UpdateInitialisedAdapters" method is for? Or are you asking what the ToArray call is for?

The ToArray call is for sort of a thread safety thing - the list of adapter services gets passed later to that "new AdaptationSandbox" call and if it just passes IEnumerable instead, the AdaptationSandbox might not enumerate the list of adapters as we passed it - the _serviceInfo contents may have changed, and IEnumerable execution is lazy, right? So, ToArray.

The "UpdateInitialisedAdapters" call is to handle registration sources (dynamic registration providers) that do type adaptation, like the functionality that provides the ability to do "container.Resolve<Func<T>>" instead of just "container.Resolve<T>". 

The opportunity I see for optimization is in the definition for ServiceRegistrationInfo (https://github.com/autofac/Autofac/blob/master/Core/Source/Autofac/Core/Registration/ServiceRegistrationInfo.cs) in the "ShouldRecalculateAdaptersOn" method. Right now it just returns true if any services from a given registration have been requested. There's even a comment in the code to that effect.

However, I don't know if that'd really be a trivial thing to fix. There'd need to be a lot of homework on it and testing to make sure there's no side effects and that we have the right solution for the problem.

So... we can't really get rid of the call, but we also probably can't fix it in a timely fashion.

What I might recommend is to refactor the tests to reuse the dependency resolver, if appropriate, or possibly make use of the lifetime scope feature - Create a base container for the unit test fixture, but on each test when you want to create the dependency resolver, do it based on a child lifetime scope instead of at the container level. If you register everything as InstancePerDependency or InstancePerLifetimeScope, once the child scope is disposed everything else will be disposed. And if you have dynamic registrations to make on the fly, you could do that with the lambda initializer on the child lifetime scope - register the common stuff in the container, register the per-test stuff in the child lifetime scope.

-T

mgu

unread,
Feb 26, 2014, 5:13:17 PM2/26/14
to aut...@googlegroups.com
Thanks for  the answer,now I just got more questions :)


Disclaimer: The project is BIG and I just started beeing a part of it, I did not write it, nor can I suggest rewriting all the test (too labour intensive). Though the refactoring part you mention, is how I have done it in the past.

The part I don't understand is that our application works, 
We are using AsSelf(), AsImplementedInterfaces, and ExternallyOwned quite  alot.
We resolve the concrete types, and interfaces either through resolve directly, contructor injection, autogenerated factories and Func<>
So; UpdateInitialisedAdapters is causing us pain, and we do not need it. 

I can see that the DefaultModules are registered:
 this.RegisterGeneric(typeof(KeyedServiceIndex<,>)).As(typeof(IIndex<,>)).InstancePerLifetimeScope();
            componentRegistry.AddRegistrationSource(new CollectionRegistrationSource());
            componentRegistry.AddRegistrationSource(new OwnedInstanceRegistrationSource());
            componentRegistry.AddRegistrationSource(new MetaRegistrationSource());
            componentRegistry.AddRegistrationSource(new LazyRegistrationSource());
            componentRegistry.AddRegistrationSource(new LazyWithMetadataRegistrationSource());
            componentRegistry.AddRegistrationSource(new StronglyTypedMetaRegistrationSource());
            componentRegistry.AddRegistrationSource(new GeneratedFactoryRegistrationSource());

I guess we could remove some of them for performance.I really just want the performance gain. I also saw the comment about optimization (which I only partly understood)

Are there any conditions where the code does not need to run at all? As it seems that our applications works without it?

Sorry, for the perhaps dumb qustion, but the internals of Autofac is like magic. Nice and clean, but too hardcore for common people.

Alex Meyer-Gleaves

unread,
Mar 19, 2014, 9:05:50 AM3/19/14
to aut...@googlegroups.com
I have thought about making the different features provided by the registration sources optional.

Did you take any measurements on how much you could save by removing unused registration sources?

Depending on the savings it might not be worthwhile.


--
You received this message because you are subscribed to the Google Groups "Autofac" group.
To unsubscribe from this group and stop receiving emails from it, send an email to autofac+u...@googlegroups.com.
To post to this group, send email to aut...@googlegroups.com.
Visit this group at http://groups.google.com/group/autofac.
For more options, visit https://groups.google.com/groups/opt_out.

Reply all
Reply to author
Forward
0 new messages