Pull to refresh

Comments 12

Я не совсем понял комментарий, вы описали ситуацию когда известен тип аргумента, а значит и известен нужный инстанс. Пост же совсем о другом, я описываю случай когда тип будет неизвестен. И код который вы указали не работает, точнее если его дописать в
Func<DateTime, long> hook = Container.Get<DateTime>().Process;

то это прямой вызов, бесполезно обернутый анонимной функцией, а не вызов посредством создания делегата метода заранее неизвестного инстанса.
Имхо, терминология в статье отличается от классической.
Называемый в статье «вызов через делегат» — в классике «вызов через expression».
Вызов через делегат в классике — это:
Func<DateTime, int> f = instance.Process; 
for(..){f(arg);}
Expression в коде служит для создания делегата, вызов метода идет именно через вызов делегата.
Вызов делегата это delegate(aaa);
А не всякие там GetType() и GetMethod() это тот же рефлекшен только в профиль
А еще есть вызов интерфейсов, вобщем прежде чем писать мегастатьи лучше погуглите codeplex с 2005 годов, много узнаете нового
Задача описанная в статье, вызов метода у заранее неизвестного инстанса. При этом метод generic. По результатам я пришел к выводу что вызывать лучше всего через dynamic или через оптимизированный вызов делегата.
Если вы знаете как вызвать более оптимально, покажите как. Я пока не нашел решения лучше.
Нда… так криво вызывать делегат — это надо постараться… нафига писать говнокод. а потом обсуждать, что он плохо работает…

Пишем:

public static TestResult TestDelegateCall(DateTime arg)
{
var instance = Container.Get(arg.GetType());
//var hook = CreateDelegate(instance, instance.GetType().GetMethod(«Process»));
Func hook = Container.Get().Process;
Stopwatch sw = new Stopwatch();
sw.Start();
long summ = 0;
for (long i = 0; i < ITERATION_COUNT; i++)
{
//summ += (long)hook.DynamicInvoke(arg);
summ += hook(arg);
}
sw.Stop();
return new TestResult { Result = summ, ElapsedMilliseconds = sw.ElapsedMilliseconds };
}
И вуаля:
Прямой вызов
Min: 921 ms
Max: 943 ms
Mean: 928,8 ms
Median: 928,5 ms

Вызов через отражение
Min: 1302 ms
Max: 1319 ms
Mean: 1311,3 ms
Median: 1310,5 ms

Вызов через делегат
Min: 921 ms
Max: 940 ms
Mean: 929,2 ms
Median: 929 ms

Вызов через делегат с оптимизациями
Min: 1093 ms
Max: 1117 ms
Mean: 1103,7 ms
Median: 1101,5 ms

Вызов через dynamic
Min: 1058 ms
Max: 1076 ms
Mean: 1066,6 ms
Median: 1065,5 ms

>(long)hook.DynamicInvoke(arg);
Это бесполезный бенчмарк. Реализация Dynamic Invoke идёт через MethodInfo.Invoke() и не отличается от reflection. Вызов делегата это вызов Invoke на нем.
Даже если типы неизвестны заранее — это еще не повод использовать DynamicInvoke! Раз уж вы используете Expression.Compile() — надо с помощью приведения типов привести делегат к заранее известной сигнатуре.

	delegate object Invoker(object target, params object[] args);
	static Invoker CreateDelegate(MethodInfo method)
	{
		var targetArg = Expression.Parameter(typeof(object));
		var argsArg = Expression.Parameter(typeof(object[]));

		Expression body = Expression.Call(
			method.IsStatic ? null : Expression.Convert(targetArg, method.DeclaringType),
			method,
			method.GetParameters().Select((p, i) => Expression.Convert(Expression.ArrayIndex(argsArg, Expression.Constant(i)), p.ParameterType)));

		if (body.Type == typeof(void))
			body = Expression.Block(body, Expression.Constant(null));
		else if (body.Type.IsValueType)
			body = Expression.Convert(body, typeof(object));

		return Expression.Lambda<Invoker>(body, targetArg, argsArg).Compile();
	}


Вот такой делегат в тесты добавьте, пожалуйста! На моих тестах он работает столь же быстро, как и прямой вызов.
Да, действительно, работает почти также быстро как прямой вызов, и быстрее чем dynamic, спасибо! Добавил в пост.
Для чего были потуги описано под спойлером.
Почему бы не использовать для бенчмарков BenchmarkDotNet?
Бенчмарк не учитывает много аспектов, которые учитываются в BenchmarkDotNet.
Почему и как правильно бенчмаркать можно узнать, например,
из вот этого видео,


в котором DreamWalker рассказывает о распространённых ошибках оценки производительности .NET-приложений.
Sign up to leave a comment.

Articles