CompiledDelegate<T> with Roslyn

imageHi, today I prepared a compilation of the delegate with Roslyn CTP, June 2012 version. It is not a big deal yet because I am trying to compile delegate more efficient. But I want to share with you results of my experiments with it. I think that compiler as a service has a great potential. I will present you a bunch of code as my favorite template – Console Application. And later I will show you a CompiledDelegate<Tin,Tout> class. The class is not finished yet and I am still working on it because of possibility of increase performance. You probably know that for example lambda exception like below

arg => arg * 2

is for example type of Expression<Func<int, int>> and that Expression<T> is great because you can invoke on it Compile method and speed up your invocation. That is part of LINQ. On the other hand we have for example something like below

arg => { return arg * 2; }

and because of curly brackets we have something very similar that works exactly the same except it is slower because type of it is for example a delegate Func<int, int>. There is no Expression Tree possible to create on it like in a first example, we are not able to compile it, and speed it up in the same moment. So, I will try to prepare something that maybe will be faster, we will see. Sorry, it is not most beautiful piece of code but it was created for measurement only. It looks like below.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace CompiledDelegateSandbox {

  class Program {

    static void Main(string[] args) {

      Func<int , int> funcA =
        cA => {
        var icA = 0;
        for (var i = 1; i < = cA; ++i) {
          var jcA = 0;
          for (var j = 1; j <= cA; ++j) {
            jcA += 1;
          }
          icA += 1;
        }
        return icA;
      };

      Expression<Func<int, int>> f = a => 1;

      Func<int , int> funcB = new CompiledDelegate<Func<int , int>>(
@"funcB",
@"
      int funcB(int cB) {
        var icB = 0;
        for (var i = 1; i < = cB; ++i) {
          var jcB = 0;
          for (var j = 1; j <= cB; ++j) {
            jcB += 1;
          }
          icB += 1;
        }
        return icB;
      }
"
      ).Delegate;

      var c = 10000;

      var meter = new Stopwatch();

      meter.Start();
      var rA = 0;
      for(var i = 0; i < 5; ++i)
        rA = funcA(c);
      meter.Stop();
      var resultA = meter.ElapsedMilliseconds;

      meter.Reset();

      meter.Start();
      var rB = 0;
      for(var i = 0; i < 5; ++i)
        rB = funcB(c);
      meter.Stop();
      var resultB = meter.ElapsedMilliseconds;

      Console.WriteLine("Results a({1}): {0} ms, b({3}): {2} ms.",
        resultA, rA, resultB, rB);

      Console.WriteLine("Press any key to close...");
      Console.ReadKey();
    }
  }
}

You maybe wonder about the result, but first I will show you CompiledDelegate<Tin,Tout> class implementation.

using Roslyn.Scripting;
using Roslyn.Scripting.CSharp;
using System;
using System.Linq.Expressions;

namespace CompiledDelegateSandbox {
  public class CompiledDelegate<Tin , Tout> {

    private readonly Func<Tin , Tout> _compiled;

    public CompiledDelegate(string methodName, string code) {

      var scriptEngine = new ScriptEngine(
        references: new[] {
          typeof(ScriptEngine).Assembly
          // SOON in ROSLYN
          //,typeof(Expression).Assembly
        },
        importedNamespaces: new string[] {
          "System"
          // SOON in ROSLYN
          //,"System.Linq.Expressions"
        }
      );

      var session = Session.Create();
      var func = "func";

      scriptEngine.Execute(code, session);

      // SOON in ROSLYN
      //var compiledCode =
      //  string.Format("new Expression<func <{0},{1}>> {2} = arg => {3}(arg); {2}.Compile(); {2}",
      //  typeof(Tin).Name, typeof(Tout).Name, func, methodName);

      var compiledCode =
        string.Format("new Func<{0},{1}>(arg => {2}(arg))",
        typeof(Tin).Name, typeof(Tout).Name, methodName);

      var compiled = scriptEngine.CompileSubmission<Func <Tin, Tout>>(compiledCode, session);

      Expression<Func <Tin, Tout>> expression = arg => compiled.Execute().Invoke(arg);
      _compiled = expression.Compile();
    }

    public Func<Tin , Tout> Delegate {
      get { return _compiled; }
    }
  }
}

I hate comments you know, but this time I used them for showing you what probably soon will be possible with Roslyn project. It may be possible in future compile Execution Tree, because a following code

arg => method(arg)

is for example type of our favorite Expression<Func<int, int>> that can be compiled and speed up. I think that my implementation of this CompiledDelegate<Tin,Tout> class is very light and you probably can understand it all, if not let me know in comments. And the last but not least I will show you an output of the performance test presented before. It works like this.

Results a(10000): 287 ms, b(10000): 433 ms.
Press any key to close...

It is not very fast, so I was a little disappointed, but I think it can be interesting for you in somehow. For trying these examples you need Visual Studio 2010/2012 SDK and Roslyn CTP June 2012. I hope you enjoy this entry.

P ;).

2 Replies to “CompiledDelegate<T> with Roslyn”

  1. I am not agree, here is a prove:

    Expression<Func<int, int>> expr1 = arg => arg * 2;
    
    Func<int, int> func1 = expr1.Compile();
    Func<int, int> func2 = arg => { return arg * 2; };
    
    var meter1 = new Stopwatch();
    meter1.Start();
    for (var i = 0; i < 10000000; ++i)
    {var r1 = func1(i);}
    meter1.Stop();
    
    var meter2 = new Stopwatch();
    meter2.Start();
    for (var i = 0; i < 10000000; ++i)
    { var r2 = func2(i); }
    meter2.Stop();
    
    Console.WriteLine("func1: {0} ms, func2: {1} ms.",
        meter1.ElapsedMilliseconds,
        meter2.ElapsedMilliseconds
    );
    Console.ReadKey();
    

    An output:

    func1: 53 ms, func2: 170 ms.
    

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.