Skip to content

Commit

Permalink
Improve property injection performance.
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmg committed May 24, 2019
1 parent 10d0e02 commit 9071c4d
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 20 deletions.
3 changes: 3 additions & 0 deletions bench/Autofac.Benchmarks/Harness.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,8 @@ public void EnumerableResolve()
{
BenchmarkRunner.Run<EnumerableResolveBenchmark>();
}

[Fact]
public void PropertyInjection() => BenchmarkRunner.Run<PropertyInjectionBenchmark>();
}
}
62 changes: 62 additions & 0 deletions bench/Autofac.Benchmarks/PropertyInjectionBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System;
using BenchmarkDotNet.Attributes;

namespace Autofac.Benchmarks
{
public class PropertyInjectionBenchmark
{
private IContainer _container;

[GlobalSetup]
public void Setup()
{
var builder = new ContainerBuilder();
builder.RegisterType<A>().PropertiesAutowired();
builder.RegisterType<B1>().PropertiesAutowired();
builder.RegisterType<B2>().PropertiesAutowired();
builder.RegisterType<C1>().PropertiesAutowired();
builder.RegisterType<C2>().PropertiesAutowired();
builder.RegisterType<D1>();
builder.RegisterType<D2>();
_container = builder.Build();
}

[Benchmark]
public void Resolve()
{
var instance = _container.Resolve<A>();
GC.KeepAlive(instance);
}

internal class A
{
public B1 B1 { get; set; }

public B2 B2 { get; set; }
}

internal class B1
{
public C1 C1 { get; set; }
}

internal class B2
{
public C2 C2 { get; set; }
}

internal class C1
{
public D1 D1 { get; set; }
}

internal class C2
{
public D2 D2 { get; set; }
}

internal class D1 { }

internal class D2 { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,27 @@
// OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac.Util;

namespace Autofac.Core.Activators.Reflection
{
internal class AutowiringPropertyInjector
internal static class AutowiringPropertyInjector
{
public const string InstanceTypeNamedParameter = "Autofac.AutowiringPropertyInjector.InstanceType";

private static readonly ConcurrentDictionary<PropertyInfo, Action<object, object>> PropertySetters =
new ConcurrentDictionary<PropertyInfo, Action<object, object>>();

private static readonly ConcurrentDictionary<Type, PropertyInfo[]> InjectableProperties =
new ConcurrentDictionary<Type, PropertyInfo[]>();

private static readonly MethodInfo CallPropertySetterOpenGenericMethod =
typeof(AutowiringPropertyInjector).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertySetter));

public static void InjectProperties(IComponentContext context, object instance, IPropertySelector propertySelector, IEnumerable<Parameter> parameters)
{
if (context == null)
Expand All @@ -57,56 +67,92 @@ public static void InjectProperties(IComponentContext context, object instance,
throw new ArgumentNullException(nameof(parameters));
}

var resolveParameters = parameters as Parameter[] ?? parameters.ToArray();

var instanceType = instance.GetType();
var injectableProperties = InjectableProperties.GetOrAdd(instanceType, type => GetInjectableProperties(type).ToArray());

foreach (var property in instanceType
.GetRuntimeProperties()
.Where(pi => pi.CanWrite))
for (var index = 0; index < injectableProperties.Length; index++)
{
var propertyType = property.PropertyType;
var property = injectableProperties[index];

if (propertyType.GetTypeInfo().IsValueType && !propertyType.GetTypeInfo().IsEnum)
if (!propertySelector.InjectProperty(property, instance))
{
continue;
}

if (propertyType.IsArray && propertyType.GetElementType().GetTypeInfo().IsValueType)
var setParameter = property.SetMethod.GetParameters()[0];
var valueProvider = (Func<object>)null;
var parameter = resolveParameters.FirstOrDefault(p => p.CanSupplyValue(setParameter, context, out valueProvider));
if (parameter != null)
{
var setter = PropertySetters.GetOrAdd(property, MakeFastPropertySetter);
setter(instance, valueProvider());
continue;
}

if (propertyType.IsGenericEnumerableInterfaceType() && propertyType.GetTypeInfo().GenericTypeArguments[0].GetTypeInfo().IsValueType)
var propertyService = new TypedService(property.PropertyType);
var instanceTypeParameter = new NamedParameter(InstanceTypeNamedParameter, instanceType);
if (context.TryResolveService(propertyService, new Parameter[] { instanceTypeParameter }, out var propertyValue))
{
var setter = PropertySetters.GetOrAdd(property, MakeFastPropertySetter);
setter(instance, propertyValue);
}
}
}

private static IEnumerable<PropertyInfo> GetInjectableProperties(Type instanceType)
{
foreach (var property in instanceType.GetRuntimeProperties())
{
if (!property.CanWrite)
{
continue;
}

if (property.GetIndexParameters().Length != 0)
var propertyType = property.PropertyType;

if (propertyType.GetTypeInfo().IsValueType && !propertyType.GetTypeInfo().IsEnum)
{
continue;
}

if (!propertySelector.InjectProperty(property, instance))
if (propertyType.IsArray && propertyType.GetElementType().GetTypeInfo().IsValueType)
{
continue;
}

var setParameter = property.SetMethod.GetParameters().First();
var valueProvider = (Func<object>)null;
var parameter = parameters.FirstOrDefault(p => p.CanSupplyValue(setParameter, context, out valueProvider));
if (parameter != null)
if (propertyType.IsGenericEnumerableInterfaceType() && propertyType.GetTypeInfo().GenericTypeArguments[0].GetTypeInfo().IsValueType)
{
property.SetValue(instance, valueProvider(), null);
continue;
}

object propertyValue;
var propertyService = new TypedService(propertyType);
var instanceTypeParameter = new NamedParameter(InstanceTypeNamedParameter, instanceType);
if (context.TryResolveService(propertyService, new Parameter[] { instanceTypeParameter }, out propertyValue))
if (property.GetIndexParameters().Length != 0)
{
property.SetValue(instance, propertyValue, null);
continue;
}

yield return property;
}
}

private static Action<object, object> MakeFastPropertySetter(PropertyInfo propertyInfo)
{
var setMethod = propertyInfo.SetMethod;
var typeInput = setMethod.DeclaringType;
var parameters = setMethod.GetParameters();
var parameterType = parameters[0].ParameterType;

// Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; }
var propertySetterAsAction = setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType));
var callPropertySetterClosedGenericMethod = CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType);
var callPropertySetterDelegate = callPropertySetterClosedGenericMethod.CreateDelegate(typeof(Action<object, object>), propertySetterAsAction);

return (Action<object, object>)callPropertySetterDelegate;
}

private static void CallPropertySetter<TDeclaringType, TValue>(
Action<TDeclaringType, TValue> setter, object target, object value) =>
setter((TDeclaringType)target, (TValue)value);
}
}

0 comments on commit 9071c4d

Please sign in to comment.