NRefactory
NRefactory copied to clipboard
Use CreateDelegate/Expression for Roslyn internals
I think it would be cool to change reflected calls to delegates for static methods and Expression for instance methods.
I found this article: http://byterot.blogspot.com/2012/05/performance-comparison-of-code.html and added "Reflected Delegate" sample... Output of program(see code below):
Static ------------------- 00:00:00.9950677 Instance ----------------- direct 00:00:00.8589729 Instance Virtual --------- 00:00:00.8558964 Reflected ---------------- 00:00:05.9739960 Reflected after binding -- current 00:00:05.0493827 Reflected Delegate ------- with change 00:00:00.8620618 Delegate ----------------- 00:00:00.8634756 Func --------------------- 00:00:00.8620104 Delegate DynamicInvoke --- 00:00:07.7965956 Expression --------------- 00:00:06.1834640 Dynamic ------------------ 00:00:01.1679985
But this is only good for static methods since .CreateDelegate is expensive to create for each instance... Instead I think Expression should be used from this article: http://www.codewrecks.com/blog/index.php/2008/10/04/expression-tree-vs-reflection/
Direct invocation 1000000 iterations 13 ms direct Reflection invocation 1000000 iterations 6705 ms current Expression Tree getter 1000000 iterations 14 ms Expression tree with unknown object getter 1000000 iterations 19 ms with change
Differences are huge... But this is maybe premature optimization. Maybe someone wants to play with performance...
using System;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
namespace PerformanceComparison
{
class Program
{
private static Random _random = new Random();
public delegate double CalculateIt(double value);
static void Main(string[] args)
{
const int TotalCount = 10000 * 1000; // 10 million
Stopwatch stopwatch = new Stopwatch();
Expression<Func<double, double>> expression =
(double value) => Math.Sin(value) / (Math.Cos(value) + 0.0000001f);
Func<double, double> func = expression.Compile();
Widget widget = new Widget();
Thread.Sleep(2000);
// ________ Static ____________________________________________________
Console.WriteLine("Static ---------------------------------------");
stopwatch.Start();
for (int i = 0; i < TotalCount; i++)
{
CallStatic(_random.NextDouble());
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
// ________ Instance ____________________________________________________
Console.WriteLine("Instance ---------------------------------------");
stopwatch.Start();
for (int i = 0; i < TotalCount; i++)
{
CallInstance(_random.NextDouble(), widget);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
// ________ Instance Virtual _______________________________________________
Console.WriteLine("Instance Virtual --------------------------------");
stopwatch.Start();
for (int i = 0; i < TotalCount; i++)
{
CallVirtualInstance(_random.NextDouble(), widget);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
// ________ Reflected ____________________________________________________
Console.WriteLine("Reflected ---------------------------------------");
stopwatch.Start();
for (int i = 0; i < TotalCount; i++)
{
CallReflector(_random.NextDouble(), widget);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
// ________ Reflected after binding ___________________________________________________
Console.WriteLine("Reflected after binding ------------------------------");
MethodInfo methodInfoInstance = typeof(Widget).GetMethod("InstanceGetTangent",
BindingFlags.Instance | BindingFlags.Public);
stopwatch.Start();
for (int i = 0; i < TotalCount; i++)
{
CallMethodInfo(_random.NextDouble(), methodInfoInstance, widget);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
// ________ Reflected Delegate ____________________________________________________
Console.WriteLine("Reflected Delegate -------------------------------------------------");
stopwatch.Start();
MethodInfo methodInfoInstance2 = typeof(Widget).GetMethod("InstanceGetTangent",
BindingFlags.Instance | BindingFlags.Public);
CalculateIt c2 = (CalculateIt)methodInfoInstance2.CreateDelegate(typeof(CalculateIt), widget);
for (int i = 0; i < TotalCount; i++)
{
CallDelegate(_random.NextDouble(), c2);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
// ________ Delegate ____________________________________________________
Console.WriteLine("Delegate -------------------------------------------------");
stopwatch.Start();
CalculateIt c = widget.InstanceGetTangent;
for (int i = 0; i < TotalCount; i++)
{
CallDelegate(_random.NextDouble(), c);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
// ________ Func ____________________________________________________
Console.WriteLine("Func -------------------------------------------------");
stopwatch.Start();
for (int i = 0; i < TotalCount; i++)
{
CallFunc(_random.NextDouble(), func);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
// ________ Delegate (Dynamic Invoke) ____________________________________________________
Console.WriteLine("Delegate DynamicInvoke ------------------------------------------");
stopwatch.Start();
for (int i = 0; i < TotalCount; i++)
{
CallDelegateDynamicInvoke(_random.NextDouble(), func);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
// ________ Expression ____________________________________________________
Console.WriteLine("Expression -------------------------------------------------");
stopwatch.Start();
for (int i = 0; i < TotalCount / 100; i++)
{
CallExpression(_random.NextDouble(), expression);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
// ________ Dynamic ____________________________________________________
Console.WriteLine("Dynamic -------------------------------------------------");
stopwatch.Start();
for (int i = 0; i < TotalCount; i++)
{
CallDynamic(_random.NextDouble(), widget);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
stopwatch.Reset();
Console.Read();
}
static void CallStatic(double value)
{
Widget.GetTangent(value);
}
static void CallInstance(double value, Widget widget)
{
widget.InstanceGetTangent(value);
}
static void CallVirtualInstance(double value, Widget widget)
{
widget.VirtualGetTangent(value);
}
static void CallReflector(double value, Widget widget)
{
MethodInfo methodInfo = typeof(Widget).GetMethod("InstanceGetTangent",
BindingFlags.Instance | BindingFlags.Public);
methodInfo.Invoke(widget, new object[] { value });
}
static void CallMethodInfo(double value, MethodInfo methodInfo, Widget widget)
{
methodInfo.Invoke(widget, new object[] { value });
}
static void CallDelegate(double value, CalculateIt d)
{
d(value);
}
static void CallFunc(double value, Func<double, double> func)
{
func(value);
}
static void CallDelegateDynamicInvoke(double value, Delegate d)
{
d.DynamicInvoke(new object[] { value });
}
static void CallExpression(double value, Expression<Func<double, double>> expression)
{
Func<double, double> compile = expression.Compile();
compile(value);
}
static void CallDynamic(double value, Widget widget)
{
dynamic d = widget;
d.InstanceGetTangent(value);
}
}
internal class WidgetBase
{
public virtual double VirtualGetTangent(double value)
{
throw new NotSupportedException();
}
}
internal class Widget : WidgetBase
{
public override double VirtualGetTangent(double value)
{
return Math.Sin(value) / (Math.Cos(value) + 0.0000001f);
}
public virtual double InstanceGetTangent(double value)
{
return Math.Sin(value) / (Math.Cos(value) + 0.0000001f);
}
public static double GetTangent(double value)
{
return Math.Sin(value) / (Math.Cos(value) + 0.0000001f);
}
}
}