NRefactory icon indicating copy to clipboard operation
NRefactory copied to clipboard

Use CreateDelegate/Expression for Roslyn internals

Open DavidKarlas opened this issue 11 years ago • 0 comments

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);
    }
  }
}

DavidKarlas avatar Jan 02 '15 13:01 DavidKarlas