2017-03-12 6 views
1

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

を実行する場合、それはコンパイル済みの表現がちょうどラムダよりもはるかに遅いことが判明しました。それは期待された結果ですか?同等のコードを得るために何とか式に書き直すことは可能ですが、どちらが速く動作しますか?

+1

http://stackoverflow.com/questions/35805609/performance-of-expression-compile-vs-lambda-direct-vs-virtual-calls –

+0

最適化を有効にしてリリースモードでテストしますか?どれくらい遅い? – Evk

+0

約3〜5倍遅くなります。 –

答えて

3

デリゲートfは、このようなタイプEとアクセス値のフィールドeでコンパイルし、生成されたクラスを使用して作成されています。第2のケースで

return <Target>.e.V; 

(表現)、デリゲートを使用し、一定の命令を使用して作成されますeが最初の要素であるオブジェクトの配列を持つターゲットとしてのクロージャ。コードは次のように表すことができます。

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

これはパフォーマンスが優れている理由です。

注:Visual Studioで "ウォッチウィンドウ"を使用すると、コードをデバッグするときに "f.Target"と "ef.Target"を検査して確認することができます。