#19476: Refactoring DefaultOrchardEventBus for performance.

Added event handler filtering using Autofac's IIndex<K,V> and named registrations.
Replaced slow MethodInfo.Invoke with very fast, strongly-typed delegates. Each delegate is created at first usage and cached.
Updated unit tests to take changes into account.

Work Item: 19476

--HG--
branch : 1.x
This commit is contained in:
Piotr Szmyd
2013-02-22 02:38:05 +01:00
parent ec3ca6738e
commit b04d9ffb32
5 changed files with 216 additions and 22 deletions

View File

@@ -20,8 +20,15 @@ namespace Orchard.Tests.Events {
var builder = new ContainerBuilder();
builder.RegisterType<DefaultOrchardEventBus>().As<IEventBus>();
builder.RegisterType<StubExceptionPolicy>().As<IExceptionPolicy>();
builder.RegisterType<StubEventHandler2>().As<IEventHandler>();
builder.RegisterInstance(_eventHandler).As<IEventHandler>();
builder.RegisterType<StubEventHandler2>()
.Named(typeof(ITestEventHandler).Name.ToLowerInvariant(), typeof(IEventHandler))
.Named(typeof(IEventHandler).Name.ToLowerInvariant(), typeof(IEventHandler))
.WithMetadata("Interfaces", typeof(StubEventHandler2).GetInterfaces().ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase));
builder.RegisterInstance(_eventHandler)
.Named(typeof(ITestEventHandler).Name.ToLowerInvariant(), typeof(IEventHandler))
.Named(typeof(IEventHandler).Name.ToLowerInvariant(), typeof(IEventHandler))
.WithMetadata("Interfaces", typeof(StubEventHandler).GetInterfaces().ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase));
_container = builder.Build();
_eventBus = _container.Resolve<IEventBus>();

View File

@@ -81,7 +81,13 @@ namespace Orchard.Environment.ShellBuilders {
}
if (typeof(IEventHandler).IsAssignableFrom(item.Type)) {
registration = registration.As(typeof(IEventHandler));
var interfaces = item.Type.GetInterfaces();
foreach (var interfaceType in interfaces) {
// Register named instance for each interface, for efficient filtering withing event bus
registration = registration.Named(interfaceType.Name.ToLowerInvariant(), typeof (IEventHandler));
}
// Keep interfaces in metadata for performance
registration = registration.WithMetadata("Interfaces", interfaces.ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase));
}
foreach (var parameter in item.Parameters) {

View File

@@ -4,16 +4,19 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac.Features.Indexed;
using Autofac.Features.Metadata;
using Orchard.Exceptions;
using Orchard.Localization;
namespace Orchard.Events {
public class DefaultOrchardEventBus : IEventBus {
private readonly Func<IEnumerable<IEventHandler>> _eventHandlers;
private readonly IIndex<string, IEnumerable<Meta<IEventHandler>>> _eventHandlers;
private readonly IExceptionPolicy _exceptionPolicy;
private static readonly ConcurrentDictionary<string, MethodInfo> _interfaceMethodsCache = new ConcurrentDictionary<string, MethodInfo>();
private static readonly ConcurrentDictionary<string, Tuple<ParameterInfo[], Func<IEventHandler, object[], object>>> _delegateCache = new ConcurrentDictionary<string, Tuple<ParameterInfo[], Func<IEventHandler, object[], object>>>();
public DefaultOrchardEventBus(Func<IEnumerable<IEventHandler>> eventHandlers, IExceptionPolicy exceptionPolicy) {
public DefaultOrchardEventBus(IIndex<string, IEnumerable<Meta<IEventHandler>>> eventHandlers, IExceptionPolicy exceptionPolicy)
{
_eventHandlers = eventHandlers;
_exceptionPolicy = exceptionPolicy;
T = NullLocalizer.Instance;
@@ -34,7 +37,7 @@ namespace Orchard.Events {
string interfaceName = parameters[0];
string methodName = parameters[1];
var eventHandlers = _eventHandlers();
var eventHandlers = _eventHandlers[interfaceName.ToLowerInvariant()];
foreach (var eventHandler in eventHandlers) {
IEnumerable returnValue;
if (TryNotifyHandler(eventHandler, messageName, interfaceName, methodName, eventData, out returnValue)) {
@@ -47,7 +50,7 @@ namespace Orchard.Events {
}
}
private bool TryNotifyHandler(IEventHandler eventHandler, string messageName, string interfaceName, string methodName, IDictionary<string, object> eventData, out IEnumerable returnValue) {
private bool TryNotifyHandler(Meta<IEventHandler> eventHandler, string messageName, string interfaceName, string methodName, IDictionary<string, object> eventData, out IEnumerable returnValue) {
try {
return TryInvoke(eventHandler, interfaceName, methodName, eventData, out returnValue);
}
@@ -61,31 +64,34 @@ namespace Orchard.Events {
}
}
private static bool TryInvoke(IEventHandler eventHandler, string interfaceName, string methodName, IDictionary<string, object> arguments, out IEnumerable returnValue) {
Type type = eventHandler.GetType();
foreach (var interfaceType in type.GetInterfaces()) {
if (String.Equals(interfaceType.Name, interfaceName, StringComparison.OrdinalIgnoreCase)) {
return TryInvokeMethod(eventHandler, interfaceType, methodName, arguments, out returnValue);
}
private static bool TryInvoke(Meta<IEventHandler> eventHandler, string interfaceName, string methodName, IDictionary<string, object> arguments, out IEnumerable returnValue) {
var matchingInterfaces = ((ILookup<string, Type>)eventHandler.Metadata["Interfaces"])[interfaceName];
foreach (var interfaceType in matchingInterfaces) {
return TryInvokeMethod(eventHandler.Value, interfaceType, methodName, arguments, out returnValue);
}
returnValue = null;
return false;
}
private static bool TryInvokeMethod(IEventHandler eventHandler, Type interfaceType, string methodName, IDictionary<string, object> arguments, out IEnumerable returnValue) {
MethodInfo method = _interfaceMethodsCache.GetOrAdd(String.Concat(eventHandler.GetType().FullName + "_" + interfaceType.Name, "_", methodName, "_", String.Join("_", arguments.Keys)), GetMatchingMethod(eventHandler, interfaceType, methodName, arguments));
var key = String.Concat(eventHandler.GetType().FullName + "_" + interfaceType.Name, "_", methodName, "_", String.Join("_", arguments.Keys));
var cachedDelegate = _delegateCache.GetOrAdd(key, k => {
var method = GetMatchingMethod(eventHandler, interfaceType, methodName, arguments);
return method != null
? Tuple.Create(method.GetParameters(), DelegateHelper.CreateDelegate<IEventHandler>(interfaceType, method))
: null;
});
if (cachedDelegate != null) {
var args = cachedDelegate.Item1.Select(methodParameter => arguments[methodParameter.Name]).ToArray();
var result = cachedDelegate.Item2(eventHandler, args);
if (method != null) {
var parameters = new List<object>();
foreach (var methodParameter in method.GetParameters()) {
parameters.Add(arguments[methodParameter.Name]);
}
var result = method.Invoke(eventHandler, parameters.ToArray());
returnValue = result as IEnumerable;
if (returnValue == null && result != null)
returnValue = new[] { result };
return true;
return true;
}
returnValue = null;
return false;
}

View File

@@ -0,0 +1,174 @@
using System;
using System.Linq;
using System.Reflection;
namespace Orchard.Events {
public static class DelegateHelper {
public static Func<T, object[], object> CreateDelegate<T>(MethodInfo method) {
return CreateDelegate<T>(typeof(T), method);
}
/// <summary>
/// Creates a strongly-typed dynamic delegate that represents a given method on a given target type.
/// </summary>
/// <remarks>
/// Provided method must be valid for the type provided as targetType.
/// </remarks>
/// <typeparam name="T">Type of the delegate target (first argument) object. Needs to be assignable from the type provided as targetType parameter.</typeparam>
/// <param name="targetType">Delegate target type.</param>
/// <param name="method">Method that the delegate represents.</param>
/// <returns>Strongly-typed delegate representing the given method.
/// First argument is the target of the delegate.
/// Second argument describes call arguments.
/// </returns>
public static Func<T, object[], object> CreateDelegate<T>(Type targetType, MethodInfo method) {
var parameters = method.ReturnType == typeof(void)
? new[] { targetType }.Concat(method.GetParameters().Select(p => p.ParameterType)).ToArray()
: new[] { targetType }.Concat(method.GetParameters().Select(p => p.ParameterType)).Concat(new[] { method.ReturnType }).ToArray();
// First fetch the generic form
MethodInfo genericHelper = method.ReturnType == typeof(void)
? typeof(DelegateHelper).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).First(m => m.Name == "BuildAction" && m.GetGenericArguments().Count() == parameters.Length)
: typeof(DelegateHelper).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).First(m => m.Name == "BuildFunc" && m.GetGenericArguments().Count() == parameters.Length);
// Now supply the type arguments
MethodInfo constructedHelper = genericHelper.MakeGenericMethod(parameters);
// Now call it. The null argument is because it's a static method.
object ret = constructedHelper.Invoke(null, new object[] { method });
return (Func<T, object[], object>)ret;
}
static Func<object, object[], object> BuildFunc<T, TRet>(MethodInfo method) {
var func = (Func<T, TRet>)Delegate.CreateDelegate(typeof(Func<T, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target);
return ret;
}
static Func<object, object[], object> BuildFunc<T, T1, TRet>(MethodInfo method) {
var func = (Func<T, T1, TRet>)Delegate.CreateDelegate(typeof(Func<T, T1, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target, (T1)p[0]);
return ret;
}
static Func<object, object[], object> BuildFunc<T, T1, T2, TRet>(MethodInfo method) {
var func = (Func<T, T1, T2, TRet>)Delegate.CreateDelegate(typeof(Func<T, T1, T2, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1]);
return ret;
}
static Func<object, object[], object> BuildFunc<T, T1, T2, T3, TRet>(MethodInfo method) {
var func = (Func<T, T1, T2, T3, TRet>)Delegate.CreateDelegate(typeof(Func<T, T1, T2, T3, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2]);
return ret;
}
static Func<object, object[], object> BuildFunc<T, T1, T2, T3, T4, TRet>(MethodInfo method) {
var func = (Func<T, T1, T2, T3, T4, TRet>)Delegate.CreateDelegate(typeof(Func<T, T1, T2, T3, T4, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3]);
return ret;
}
static Func<object, object[], object> BuildFunc<T, T1, T2, T3, T4, T5, TRet>(MethodInfo method) {
var func = (Func<T, T1, T2, T3, T4, T5, TRet>)Delegate.CreateDelegate(typeof(Func<T, T1, T2, T3, T4, T5, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4]);
return ret;
}
static Func<object, object[], object> BuildFunc<T, T1, T2, T3, T4, T5, T6, TRet>(MethodInfo method) {
var func = (Func<T, T1, T2, T3, T4, T5, T6, TRet>)Delegate.CreateDelegate(typeof(Func<T, T1, T2, T3, T4, T5, T6, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5]);
return ret;
}
static Func<object, object[], object> BuildFunc<T, T1, T2, T3, T4, T5, T6, T7, TRet>(MethodInfo method) {
var func = (Func<T, T1, T2, T3, T4, T5, T6, T7, TRet>)Delegate.CreateDelegate(typeof(Func<T, T1, T2, T3, T4, T5, T6, T7, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6]);
return ret;
}
static Func<object, object[], object> BuildFunc<T, T1, T2, T3, T4, T5, T6, T7, T8, TRet>(MethodInfo method) {
var func = (Func<T, T1, T2, T3, T4, T5, T6, T7, T8, TRet>)Delegate.CreateDelegate(typeof(Func<T, T1, T2, T3, T4, T5, T6, T7, T8, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7]);
return ret;
}
static Func<object, object[], object> BuildFunc<T, T1, T2, T3, T4, T5, T6, T7, T8, T9, TRet>(MethodInfo method) {
var func = (Func<T, T1, T2, T3, T4, T5, T6, T7, T8, T9, TRet>)Delegate.CreateDelegate(typeof(Func<T, T1, T2, T3, T4, T5, T6, T7, T8, T9, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7], (T9)p[8]);
return ret;
}
static Func<object, object[], object> BuildFunc<T, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRet>(MethodInfo method) {
var func = (Func<T, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRet>)Delegate.CreateDelegate(typeof(Func<T, TRet>), method);
Func<object, object[], object> ret = (target, p) => func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7], (T9)p[8], (T10)p[9]);
return ret;
}
static Func<object, object[], object> BuildAction<T>(MethodInfo method) {
var func = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target); return null; };
return ret;
}
static Func<object, object[], object> BuildAction<T, T1>(MethodInfo method) {
var func = (Action<T, T1>)Delegate.CreateDelegate(typeof(Action<T, T1>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target, (T1)p[0]); return null; };
return ret;
}
static Func<object, object[], object> BuildAction<T, T1, T2>(MethodInfo method) {
var func = (Action<T, T1, T2>)Delegate.CreateDelegate(typeof(Action<T, T1, T2>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1]); return null; };
return ret;
}
static Func<object, object[], object> BuildAction<T, T1, T2, T3>(MethodInfo method) {
var func = (Action<T, T1, T2, T3>)Delegate.CreateDelegate(typeof(Action<T, T1, T2, T3>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2]); return null; };
return ret;
}
static Func<object, object[], object> BuildAction<T, T1, T2, T3, T4>(MethodInfo method) {
var func = (Action<T, T1, T2, T3, T4>)Delegate.CreateDelegate(typeof(Action<T, T1, T2, T3, T4>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3]); return null; };
return ret;
}
static Func<object, object[], object> BuildAction<T, T1, T2, T3, T4, T5>(MethodInfo method) {
var func = (Action<T, T1, T2, T3, T4, T5>)Delegate.CreateDelegate(typeof(Action<T, T1, T2, T3, T4, T5>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4]); return null; };
return ret;
}
static Func<object, object[], object> BuildAction<T, T1, T2, T3, T4, T5, T6>(MethodInfo method) {
var func = (Action<T, T1, T2, T3, T4, T5, T6>)Delegate.CreateDelegate(typeof(Action<T, T1, T2, T3, T4, T5, T6>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5]); return null; };
return ret;
}
static Func<object, object[], object> BuildAction<T, T1, T2, T3, T4, T5, T6, T7>(MethodInfo method) {
var func = (Action<T, T1, T2, T3, T4, T5, T6, T7>)Delegate.CreateDelegate(typeof(Action<T, T1, T2, T3, T4, T5, T6, T7>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6]); return null; };
return ret;
}
static Func<object, object[], object> BuildAction<T, T1, T2, T3, T4, T5, T6, T7, T8>(MethodInfo method) {
var func = (Action<T, T1, T2, T3, T4, T5, T6, T7, T8>)Delegate.CreateDelegate(typeof(Action<T, T1, T2, T3, T4, T5, T6, T7, T8>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7]); return null; };
return ret;
}
static Func<object, object[], object> BuildAction<T, T1, T2, T3, T4, T5, T6, T7, T8, T9>(MethodInfo method) {
var func = (Action<T, T1, T2, T3, T4, T5, T6, T7, T8, T9>)Delegate.CreateDelegate(typeof(Action<T, T1, T2, T3, T4, T5, T6, T7, T8, T9>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7], (T9)p[8]); return null; };
return ret;
}
static Func<object, object[], object> BuildAction<T, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(MethodInfo method) {
var func = (Action<T, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>)Delegate.CreateDelegate(typeof(Action<T>), method);
Func<object, object[], object> ret = (target, p) => { func((T)target, (T1)p[0], (T2)p[1], (T3)p[2], (T4)p[3], (T5)p[4], (T6)p[5], (T7)p[6], (T8)p[7], (T9)p[8], (T10)p[9]); return null; };
return ret;
}
}
}

View File

@@ -227,6 +227,7 @@
<Compile Include="Environment\WorkContextImplementation.cs" />
<Compile Include="Environment\WorkContextModule.cs" />
<Compile Include="Environment\WorkContextProperty.cs" />
<Compile Include="Events\DelegateHelper.cs" />
<Compile Include="Exceptions\DefaultExceptionPolicy.cs" />
<Compile Include="Exceptions\ExceptionExtensions.cs" />
<Compile Include="Exceptions\Filters\UnhandledExceptionFilter.cs" />