title: Compiled lambda expressions used as property getters and setters: wrong benchmarking method or wrong lambda expression construction?
body: <p>Hi I was trying out using (cached) compiled lambda expressions for properties access, for sure I got a result much better (i.e. faster) than when using PropertyInfo.GetValue()/SetValue() method calls. However, I feel it's still really far getting close to the &quot;native&quot; properties speed. Is it the benchmarking method that makes the results so different from what others could get?</p>
  <ul>
  <li><a href="https://www.codeproject.com/Articles/584720/ExpressionplusbasedplusPropertyplusGettersplusandp" rel="nofollow noreferrer">https://www.codeproject.com/Articles/584720/ExpressionplusbasedplusPropertyplusGettersplusandp</a></li>
  <li><a href="http://www.palmmedia.de/Blog/2012/2/4/reflection-vs-compiled-expressions-vs-delegates-performance-comparision" rel="nofollow noreferrer">http://www.palmmedia.de/Blog/2012/2/4/reflection-vs-compiled-expressions-vs-delegates-performance-comparision</a></li>
  <li><a href="https://stackoverflow.com/questions/5568294/compiled-c-sharp-lambda-expressions-performance">Compiled C# Lambda Expressions Performance</a></li>
  </ul>
  <p>Here is below the result I got after running my piece of code below:</p>
  <blockquote>
  <pre><code>Native: Elapsed = 00:00:00.0995876 (99.5876 ms); Step = 1.992E-005 ms
  Lambda Expression: Elapsed = 00:00:00.5369273 (536.9273 ms); Step = 1.074E-004 ms
  Property Info: Elapsed = 00:00:01.9187312 (1918.7312 ms); Step = 3.837E-004 ms
  
  1.000 &lt; 5.392 &lt; 19.267
  </code></pre>
  </blockquote>
  <p>Honestly I feel that based on others benchmarks, the compiled lambda expressions should be twice slower than using regular properties, not like between 5 - 6 times slower.</p>
  <p>Any thought? The bench-marking method? The way the compiled lambda expression is computed?</p>
  <pre><code>public static class Program
  {
      public static void Main(params string[] args)
      {
          var stepCount = 5000000UL;
  
          var dummy = new Dummy();
  
          const string propertyName = &quot;Soother&quot;;
  
          const bool propertyValue = true;
  
          var propertyInfo = typeof(Dummy).GetProperty(propertyName);
  
          var nativeBenchmark = Benchmark.Run(&quot;Native&quot;, stepCount, () =&gt; dummy.Soother = propertyValue);
          var lambdaExpressionBenchmark = Benchmark.Run(&quot;Lambda Expression&quot;, stepCount, () =&gt; dummy.Set(propertyName, propertyValue));
          var propertyInfoBenchmark = Benchmark.Run(&quot;Property Info&quot;, stepCount, () =&gt; propertyInfo.SetValue(dummy, propertyValue, null));
  
          var benchmarkReports = new[] { nativeBenchmark, lambdaExpressionBenchmark, propertyInfoBenchmark }.OrderBy(item =&gt; item.ElapsedMilliseconds);
  
          benchmarkReports.Join(Environment.NewLine).WriteLineToConsole();
  
          var fastest = benchmarkReports.First().ElapsedMilliseconds;
  
          benchmarkReports.Select(report =&gt; (report.ElapsedMilliseconds / fastest).ToString(&quot;0.000&quot;)).Join(&quot; &lt; &quot;).WriteLineToConsole();
  
          Console.ReadKey();
      }
  }
  
  public class Dummy
  {
      public bool? Soother { get; set; } = true;
  }
  
  public class BenchMarkReport
  {
      #region Fields &amp; Properties
  
      public string Name { get; }
      public TimeSpan ElapsedTime { get; }
      public double ElapsedMilliseconds
      {
          get
          {
              return ElapsedTime.TotalMilliseconds;
          }
      }
      public ulong StepCount { get; }
      public double StepElapsedMilliseconds
      {
          get
          {
              return ElapsedMilliseconds / StepCount;
          }
      }
  
      #endregion
  
      #region Constructors
  
      internal BenchMarkReport(string name, TimeSpan elapsedTime, ulong stepCount)
      {
          Name = name;
          ElapsedTime = elapsedTime;
          StepCount = stepCount;
      }
  
      #endregion
  
      #region Methods
  
      public override string ToString()
      {
          return $&quot;{Name}: Elapsed = {ElapsedTime} ({ElapsedMilliseconds} ms); Step = {StepElapsedMilliseconds:0.###E+000} ms&quot;;
      }
  
      #endregion
  }
  
  public class Benchmark
  {
      #region Fields &amp; Properties
  
      private readonly Action _stepAction;
  
      public string Name { get; }
  
      public ulong StepCount { get; }
  
      public Benchmark(string name, ulong stepCount, Action stepAction)
      {
          Name = name;
          StepCount = stepCount;
          _stepAction = stepAction;
      }
  
      #endregion
  
      #region Constructors
  
      #endregion
  
      #region Methods
  
      public static BenchMarkReport Run(string name, ulong stepCount, Action stepAction)
      {
          var benchmark = new Benchmark(name, stepCount, stepAction);
  
          var benchmarkReport = benchmark.Run();
  
          return benchmarkReport;
      }
  
      public BenchMarkReport Run()
      {
          return Run(StepCount);
      }
  
      public BenchMarkReport Run(ulong stepCountOverride)
      {
          var stopwatch = Stopwatch.StartNew();
  
          for (ulong i = 0; i &lt; StepCount; i++)
          {
              _stepAction();
          }
  
          stopwatch.Stop();
  
          var benchmarkReport = new BenchMarkReport(Name, stopwatch.Elapsed, stepCountOverride);
  
          return benchmarkReport;
      }
  
      #endregion
  }
  
  public static class ObjectExtensions
  {
      public static void WriteToConsole&lt;TInstance&gt;(this TInstance instance)
      {
          Console.Write(instance);
      }
  
      public static void WriteLineToConsole&lt;TInstance&gt;(this TInstance instance)
      {
          Console.WriteLine(instance);
      }
  
      // Goodies: add name inference from property lambda expression
      // e.g. &quot;instance =&gt; instance.PropertyName&quot; redirected using &quot;PropertyName&quot;
  
      public static TProperty Get&lt;TInstance, TProperty&gt;(this TInstance instance, string propertyName)
      {
          return FastPropertyRepository&lt;TInstance, TProperty&gt;.GetGetter(propertyName)(instance);
      }
  
      public static void Set&lt;TInstance, TProperty&gt;(this TInstance instance, string propertyName, TProperty propertyValue)
      {
          FastPropertyRepository&lt;TInstance, TProperty&gt;.GetSetter(propertyName)(instance, propertyValue);
      }
  }
  
  public static class EnumerableExtensions
  {
      public static string Join&lt;TSource&gt;(this IEnumerable&lt;TSource&gt; source, string separator = &quot;, &quot;)
      {
          return string.Join(separator, source);
      }
  }
  
  internal static class FastPropertyRepository&lt;TInstance, TProperty&gt;
  {
      private static readonly IDictionary&lt;string, Action&lt;TInstance, TProperty&gt;&gt; Setters;
      private static readonly IDictionary&lt;string, Func&lt;TInstance, TProperty&gt;&gt; Getters;
  
      static FastPropertyRepository()
      {
          Getters = new ConcurrentDictionary&lt;string, Func&lt;TInstance, TProperty&gt;&gt;();
          Setters = new ConcurrentDictionary&lt;string, Action&lt;TInstance, TProperty&gt;&gt;();
      }
  
      public static Func&lt;TInstance, TProperty&gt; GetGetter(string propertyName)
      {
          Func&lt;TInstance, TProperty&gt; getter;
          if (!Getters.TryGetValue(propertyName, out getter))
          {
              getter = FastPropertyFactory.GeneratePropertyGetter&lt;TInstance, TProperty&gt;(propertyName);
              Getters[propertyName] = getter;
          }
  
          return getter;
      }
  
      public static Action&lt;TInstance, TProperty&gt; GetSetter(string propertyName)
      {
          Action&lt;TInstance, TProperty&gt; setter;
          if (!Setters.TryGetValue(propertyName, out setter))
          {
              setter = FastPropertyFactory.GeneratePropertySetter&lt;TInstance, TProperty&gt;(propertyName);
              Setters[propertyName] = setter;
          }
  
          return setter;
      }
  }
  
  internal static class FastPropertyFactory
  {
      public static Func&lt;TInstance, TProperty&gt; GeneratePropertyGetter&lt;TInstance, TProperty&gt;(string propertyName)
      {
          var parameterExpression = Expression.Parameter(typeof(TInstance), &quot;value&quot;);
  
          var propertyValueExpression = Expression.Property(parameterExpression, propertyName);
  
          var expression = propertyValueExpression.Type == typeof(TProperty) ? propertyValueExpression : (Expression)Expression.Convert(propertyValueExpression, typeof(TProperty));
  
          var propertyGetter = Expression.Lambda&lt;Func&lt;TInstance, TProperty&gt;&gt;(expression, parameterExpression).Compile();
  
          return propertyGetter;
      }
  
      public static Action&lt;TInstance, TProperty&gt; GeneratePropertySetter&lt;TInstance, TProperty&gt;(string propertyName)
      {
          var instanceParameterExpression = Expression.Parameter(typeof(TInstance));
  
          var parameterExpression = Expression.Parameter(typeof(TProperty), propertyName);
  
          var propertyValueExpression = Expression.Property(instanceParameterExpression, propertyName);
  
          var conversionExpression = propertyValueExpression.Type == typeof(TProperty) ? parameterExpression : (Expression)Expression.Convert(parameterExpression, propertyValueExpression.Type);
  
          var propertySetter = Expression.Lambda&lt;Action&lt;TInstance, TProperty&gt;&gt;(Expression.Assign(propertyValueExpression, conversionExpression), instanceParameterExpression, parameterExpression).Compile();
  
          return propertySetter;
      }
  }
  </code></pre>