|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Reflection; |
|
using BenchmarkDotNet.Attributes; |
|
using BenchmarkDotNet.Running; |
|
|
|
class Program |
|
{ |
|
static void Main(string[] args) |
|
{ |
|
BenchmarkRunner.Run<CtorTests>(); |
|
BenchmarkRunner.Run<RunTests>(); |
|
} |
|
} |
|
|
|
[MemoryDiagnoser] |
|
public class CtorTests { |
|
[Benchmark(Baseline = true)] |
|
public Calculator Calculator() { |
|
return new Calculator(); |
|
} |
|
[Benchmark] |
|
public IMethodInvoker Switch() { |
|
var instance = new Calculator(); |
|
return new SwitchMethodInvoker(instance); |
|
} |
|
[Benchmark] |
|
public IMethodInvoker Reflection() { |
|
var instance = new Calculator(); |
|
return new ReflectionMethodInvoker(instance); |
|
} |
|
[Benchmark] |
|
public object CachedReflection() { |
|
var instance = new Calculator(); |
|
return new CachedReflectionMethodInvoker(instance); |
|
} |
|
[Benchmark] |
|
public object Delegate() { |
|
var instance = new Calculator(); |
|
return new DelegateMethodInvoker(instance); |
|
} |
|
[Benchmark] |
|
public object GenericDelegateAdd() { |
|
var instance = new Calculator(); |
|
return new GenericDelegateMethodInvoker(instance); |
|
} |
|
} |
|
|
|
[MemoryDiagnoser] |
|
public class RunTests { |
|
static Calculator _instance; |
|
static SwitchMethodInvoker _calculator; |
|
static ReflectionMethodInvoker _reflection; |
|
static CachedReflectionMethodInvoker _cachedReflection; |
|
static DelegateMethodInvoker _delegate; |
|
static GenericDelegateMethodInvoker _genericDelegate; |
|
[GlobalSetup] |
|
public static void GlobalSetup() { |
|
_instance = new Calculator(); |
|
_calculator = new SwitchMethodInvoker(_instance); |
|
_reflection = new ReflectionMethodInvoker(_instance); |
|
_cachedReflection = new CachedReflectionMethodInvoker(_instance); |
|
_delegate = new DelegateMethodInvoker(_instance); |
|
_genericDelegate = new GenericDelegateMethodInvoker(_instance); |
|
} |
|
[Benchmark(Baseline = true)] |
|
public object Add() { |
|
return _instance.Add(1, 2); |
|
} |
|
[Benchmark] |
|
public object SwitchAdd() { |
|
var args = new object[] { 1, 2 }; |
|
return _calculator.Invoke("Add", args); |
|
} |
|
[Benchmark] |
|
public object ReflectionAdd() { |
|
var args = new object[] { 1, 2 }; |
|
return _reflection.Invoke(nameof(Calculator.Add), args); |
|
} |
|
[Benchmark] |
|
public object CachedReflectionAdd() { |
|
var args = new object[] { 1, 2 }; |
|
return _cachedReflection.Invoke(nameof(Calculator.Add), args); |
|
} |
|
[Benchmark] |
|
public object DelegateAdd() { |
|
var args = new object[] { 1, 2 }; |
|
return _delegate.Invoke(nameof(Calculator.Add), args); |
|
} |
|
[Benchmark] |
|
public object GenericDelegateAdd() { |
|
var args = new object[] { 1, 2 }; |
|
return _genericDelegate.Invoke(nameof(Calculator.Add), args); |
|
} |
|
} |
|
|
|
public interface IMethodInvoker { |
|
object Invoke(string name, object[] args); |
|
} |
|
|
|
public class SwitchMethodInvoker : IMethodInvoker { |
|
readonly Calculator _instance; |
|
public SwitchMethodInvoker(Calculator instance) => |
|
_instance = instance; |
|
|
|
public object Invoke(string name, object[] args) |
|
{ |
|
return name switch { |
|
"Subtract" => _instance.Subtract((int)args[0], (int)args[1]), |
|
"Add" => _instance.Add((int)args[0], (int)args[1]), |
|
_ => throw new ArgumentOutOfRangeException(nameof(name)), |
|
}; |
|
} |
|
} |
|
|
|
public class ReflectionMethodInvoker : IMethodInvoker |
|
{ |
|
readonly object _instance; |
|
public ReflectionMethodInvoker(object instance) { |
|
_instance = instance; |
|
} |
|
public object Invoke(string name, object[] args) |
|
{ |
|
return _instance.GetType().GetMethod(name).Invoke(_instance, args); |
|
} |
|
} |
|
|
|
public class CachedReflectionMethodInvoker : IMethodInvoker { |
|
readonly object _instance; |
|
readonly ILookup<string, MethodInfo> _methods; |
|
public CachedReflectionMethodInvoker(object instance) { |
|
_instance = instance; |
|
_methods = _instance.GetType().GetMethods().ToLookup(x => x.Name); |
|
} |
|
public object Invoke(string name, object[] args) |
|
{ |
|
return _methods[name].First().Invoke(_instance, args); |
|
} |
|
} |
|
|
|
public class DelegateMethodInvoker : IMethodInvoker { |
|
readonly ILookup<string, Delegate> _delegates; |
|
static readonly Dictionary<int, Type> _funcs = typeof(Func<>).Assembly |
|
.GetTypes() |
|
.Where(x => x.IsGenericType) |
|
.Where(x => x.Name.StartsWith("Func`")) |
|
.ToDictionary(x => x.GetGenericArguments().Length); |
|
public DelegateMethodInvoker(object instance) { |
|
_delegates = instance.GetType() |
|
.GetMethods() |
|
.ToLookup( |
|
x => x.Name, |
|
x => { |
|
var types = new List<Type>(); |
|
types.AddRange(x.GetParameters().Select(y => y.ParameterType)); |
|
types.Add(x.ReturnType); |
|
|
|
var delegateType = _funcs[types.Count].MakeGenericType(types.ToArray()); |
|
return x.CreateDelegate(delegateType, instance); |
|
} |
|
); |
|
} |
|
public object Invoke(string name, object[] args) |
|
{ |
|
return _delegates[name].First().DynamicInvoke(args); |
|
} |
|
} |
|
|
|
public class GenericDelegateMethodInvoker : IMethodInvoker |
|
{ |
|
readonly Dictionary<string, IInvoker> _invokers; |
|
static readonly Dictionary<int, Type> _invokerTypes = new Dictionary<int, Type> { |
|
[0] = typeof(Invoker<,>), |
|
[1] = typeof(Invoker<,,>), |
|
[2] = typeof(Invoker<,,,>), |
|
}; |
|
public GenericDelegateMethodInvoker(object instance) { |
|
_invokers = instance.GetType().GetMethods() |
|
.ToDictionary( |
|
method => method.Name, |
|
method => { |
|
var types = new List<Type>(); |
|
types.Add(method.DeclaringType); |
|
var paramTypes = method.GetParameters().Select(x => x.ParameterType).ToList(); |
|
var invokerType = _invokerTypes[paramTypes.Count]; |
|
types.AddRange(paramTypes); |
|
types.Add(method.ReturnType); |
|
return (IInvoker)Activator.CreateInstance( |
|
invokerType.MakeGenericType(types.ToArray()), |
|
new object[] { instance, method } |
|
); |
|
} |
|
); |
|
} |
|
public object Invoke(string name, object[] args) |
|
{ |
|
return _invokers[name].Invoke(args); |
|
} |
|
|
|
interface IInvoker { |
|
object Invoke(object[] args); |
|
} |
|
|
|
class Invoker<TInstance, TResult> : IInvoker { |
|
readonly Func<TResult> _fn; |
|
public Invoker(TInstance instance, MethodInfo method) { |
|
_fn = method.CreateDelegate<Func<TResult>>(instance); |
|
} |
|
|
|
public object Invoke(object[] args) { |
|
return _fn(); |
|
} |
|
} |
|
|
|
class Invoker<TInstance, TArg, TResult> : IInvoker { |
|
readonly Func<TArg, TResult> _fn; |
|
public Invoker(TInstance instance, MethodInfo method) { |
|
_fn = method.CreateDelegate<Func<TArg, TResult>>(instance); |
|
} |
|
|
|
public object Invoke(object[] args) { |
|
return _fn((TArg)args[0]); |
|
} |
|
} |
|
|
|
class Invoker<TInstance, TArg1, TArg2, TResult> : IInvoker { |
|
readonly Func<TArg1, TArg2, TResult> _fn; |
|
public Invoker(TInstance instance, MethodInfo method) { |
|
_fn = method.CreateDelegate<Func<TArg1, TArg2, TResult>>(instance); |
|
} |
|
|
|
public object Invoke(object[] args) { |
|
return _fn((TArg1)args[0], (TArg2)args[1]); |
|
} |
|
} |
|
} |
|
|
|
public class Calculator { |
|
public int Add(int x, int y) { |
|
return x + y; |
|
} |
|
public int Subtract(int x, int y) { |
|
return x - y; |
|
} |
|
} |