Eugene S
Eugene S

Reputation: 83

Compiled expression tree performance

If to run following code

using System;
using System.Linq.Expressions;
using System.Diagnostics;

public class E
{
    public double V { get; set; }
}

public class Program
{
    public static void Main()
    {
        E e = new E();
        Func<double> f = () => e.V;

        Expression expr = Expression.Property(Expression.Constant(e), "V");
        Expression<Func<double>> exp = Expression.Lambda<Func<double>>(expr);
        Func<double> ef = exp.Compile();

        e.V = 123;

        int attempts = 5;
        for (int j = 0; j < attempts; j++)
        {
            int c = 100000;

            double[] r1 = new double[c];
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < c; i++)
            {
                r1[i] = f();
            }
            sw.Stop();

            double[] r2 = new double[c];
            Stopwatch sw2 = new Stopwatch();
            sw2.Start();
            for (int i = 0; i < c; i++)
            {
                r2[i] = ef();
            }
            sw2.Stop();

            double rat = (double)sw.ElapsedTicks / sw2.ElapsedTicks;

            Console.WriteLine(rat);
        }
    }
}

then it turns out that compiled expression is much slower than just a lambda. Is it expected result? Is it possible to rewrite to expression somehow to get equivalent code but which will work faster?

Upvotes: 1

Views: 804

Answers (1)

Tony THONG
Tony THONG

Reputation: 772

your delegate f is created with a compiled generated class with a field e of type E and access value like this :

return <Target>.e.V;

In the second case (expression), delegate is created using constant instruction that use a Closure as target with an array of object where e is the first element. Code can be represented like this :

return ((E)<Target>.Constants[0]).V;

That's why performance is better for first case.

note : with "Watch window" in Visual Studio, when you debug the code, you can inspect "f.Target" and "ef.Target" to confirm it.

Upvotes: 4

Related Questions