Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove closure allocations from ServiceCollectionDescriptorExtensions #44696

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace Microsoft.Extensions.DependencyInjection.Extensions
{
Expand Down Expand Up @@ -85,10 +84,17 @@ public static void TryAdd(
throw new ArgumentNullException(nameof(descriptor));
}

if (!collection.Any(d => d.ServiceType == descriptor.ServiceType))
int count = collection.Count;
for (int i = 0; i < count; i++)
{
collection.Add(descriptor);
if (collection[i].ServiceType == descriptor.ServiceType)
{
// Already added
return;
}
Copy link
Member

@stephentoub stephentoub Nov 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How many times are these methods called in a typical startup? The fact that you said it's saving ~750 closures suggests it's called to add that many services, and this is O(N^2) (for each service looking at every other service already added)?

Copy link
Member Author

@benaadams benaadams Nov 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep definitely is; can't get the call counts atm as Tracing in dotTrace is broken for me atm 😢

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be good to understand the actual cost. While saving 750 allocations is good, I'd expect saving 250,000 interface dispatches to be better ;-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The service collection needs to store a dictionary and introduce a new interface to check if a service type already exists

}

collection.Add(descriptor);
}

/// <summary>
Expand Down Expand Up @@ -608,12 +614,19 @@ public static void TryAddEnumerable(
nameof(descriptor));
}

if (!services.Any(d =>
d.ServiceType == descriptor.ServiceType &&
d.GetImplementationType() == implementationType))
int count = services.Count;
for (int i = 0; i < count; i++)
{
services.Add(descriptor);
ServiceDescriptor service = services[i];
if (service.ServiceType == descriptor.ServiceType &&
service.GetImplementationType() == implementationType)
{
// Already added
return;
}
}

services.Add(descriptor);
}

/// <summary>
Expand Down Expand Up @@ -674,10 +687,15 @@ public static IServiceCollection Replace(
throw new ArgumentNullException(nameof(descriptor));
}

ServiceDescriptor? registeredServiceDescriptor = collection.FirstOrDefault(s => s.ServiceType == descriptor.ServiceType);
if (registeredServiceDescriptor != null)
// Remove existing
int count = collection.Count;
for (int i = 0; i < count; i++)
{
collection.Remove(registeredServiceDescriptor);
if (collection[i].ServiceType == descriptor.ServiceType)
{
collection.RemoveAt(i);
break;
}
}

collection.Add(descriptor);
Expand Down