2016-04-30 11 views
1

私は多くのプロパティを実行時に動的にコピーするエンジンで作業しています。状況によっては、途中でプロパティの値を変更する場合と変更しない場合があります。もともとは反射で書かれていましたが、パフォーマンスの問題により、最近Reflection.Emitに書き直しました。書き直しは完了し、パフォーマンスは明らかに改善されましたが、コードは手書きのC#に対してベンチマークされています。明らかに、公平な戦いになるためには、ベンチマーク用の手書きのC#ILとして「類似の機能」を持っています。C#EMIT ILのパフォーマンスの問題

ILエンジンの一部は、フライングカラーで渡されたためサインオフされており、手書きのC#でほぼ1:1です。これは私に語った:

  1. 私たちの一般的な概念と実装は

  2. ベンチマークは

  3. ILと手書きC#が正しい正しい動的メソッドを呼び出すには、オーバーヘッドはありません全く同じ方法でテストされているので、面白いことはありませんJITビジネスが進行中です(私は思っていません)

私たちはILが手書きでやや遅くなることを期待していましたが、これまでのところこれまでのところはそうではありません。長いラウンドで数ms遅くなるかもしれませんが、ILにショートカットを入れることができますので、差分を補うのに役立ちます。

ある特定のケースでは、それは実質的に遅くなります。 2倍遅くなります。 C#

、あなたが持っていると思います:

class Source 
{ 
    public string S1 { get; set; } 
    public int I1 { get; set; } 
    public int I2 { get; set; } 
    public double D1 { get; set; } 
    public double D2 { get; set; } 
    public double D3 { get; set; } 
} 

class Dest 
{ 
    public string S1 { get; set; } 
    public int I1 { get; set; } 
    public string I2 { get; set; } 
    public double D1 { get; set; } 
    public int D2 { get; set; } 
    public string D3 { get; set; } 
} 

static Dest Test(Source s) 
{ 
    Dest d = new Dest(); 

    object o = s.D3; 

    if (o != null) 
     d.D3 = o.ToString(); 

    return d; 
} 

これは私が同様の機能によってどのような意味です。ジェネリックであるために、プロパティを文字列にコピーするときは、最初にボックスに入れてObject.ToString()を呼び出します。ネイティブでは、値のタイプはToStringと異なるので、上記のコードをリンゴにリンゴにします。

D3コピー/ ToStringと他の5つのプロパティのコメントを外すと、C#で1:1に戻ります。

あなたはI2intであることがわかります - >stringが、いくつかの理由で、1つはdoubleと同じ問題を抱えていません - >string。私はその倍を得るToString()一般的にはより高価ですが、その費用はC#のコードにも表示されるはずですが、そうではありません。

D3コピー用のコードは、I2コピー用のコードと同じです。なぜD3コピーの巨大なオーバーヘッドですか?

EDIT:

コンパイラが発する:

IL_0000: newobj instance void ConsoleApplication3.Dest::.ctor() 
    IL_0005: ldarg.0 
    IL_0006: callvirt instance float64 ConsoleApplication3.Source::get_D3() 
    IL_000b: box [mscorlib]System.Double 
    IL_0010: stloc.0 
    IL_0011: dup 
    IL_0012: ldloc.0 
    IL_0013: brtrue.s IL_0018 

    IL_0015: ldnull 
    IL_0016: br.s IL_001e 

    IL_0018: ldloc.0 
    IL_0019: callvirt instance string [mscorlib]System.Object::ToString() 

    IL_001e: callvirt instance void ConsoleApplication3.Dest::set_D3(string) 
    IL_0023: ret 

私のコードのこの特定のセクションは、他の場所で行うのDestはオブジェクトのための新しいを放出しません。上記のC#に見られるように、dupはDestオブジェクトを捨てています。

LocalBuilder localBuilderObject = generator.DeclareLocal(_typeOfObject); 

Label labelNull = generator.DefineLabel(); 
Label labelNotNull = generator.DefineLabel(); 

generator.Emit(OpCodes.Ldarg_0); 
generator.Emit(OpCodes.Callvirt, miGetter); 
generator.Emit(OpCodes.Box, typeSource); 
generator.Emit(OpCodes.Stloc_S, localBuilderObject); 
generator.Emit(OpCodes.Dup); 
generator.Emit(OpCodes.Ldloc_S, localBuilderObject); 
generator.Emit(OpCodes.Brtrue, labelNotNull); 
generator.Emit(OpCodes.Ldnull); 
generator.Emit(OpCodes.Br, labelNull); 
generator.MarkLabel(labelNotNull); 
generator.Emit(OpCodes.Ldloc_S, localBuilderObject); 
generator.Emit(OpCodes.Callvirt, _miToString); 
generator.MarkLabel(labelNull); 
generator.Emit(OpCodes.Callvirt,miSetter); 

私が述べたように、私は値の型を気にすることなく、一般的にObject::ToString()を呼び出すことができますので、私はタイプボックス。 Ref型もこのパスを通過します。 C#コードはこのように振る舞い、時間は1/2になります???

私は今週末この問題を悩ましています。さらなるテストでは、他の値のタイプは1:1です。 int,longなど何らかの理由でdoubleが問題を引き起こしています。

+0

あなたのコードはちょっと難解です:_this_引数をロードし、フィールド値を取得し、それをボックスに格納してからローカルに格納します。あなたは何を複製しますか?また、あなたが発行する実際のILと、C#がそのバージョンに対して生成する実際のILを含めてください。 –

+0

@ 500-InternalServerError、応答に感謝します。私はILラッパーを取り除くためにオリジナルのポストを更新しました(ただし、他のオペコードや何も追加しません)。複製はDestオブジェクトです。いくつかのdiffs私のコード:stloc_s/ldloc_sの代わりにstloc.0/ldloc.0と分岐対ブランチショート。 Int/longはC#のパフォーマンスが賢明なので1:1です。 – SledgeHammer

+0

このレベルのマイクロベンチマーキングでは、ページ上のコードの位置合わせなどの愚かなことは、あなたを上手くいくのに十分です。また、ジッタによって生成された実際のコードにドリルダウンして、実際の違いがないかどうかを確認する必要があります。単にILを見つめ続けるだけでは助けにならないでしょう。 –

答えて

0

ダブルジャンプの代わりにnull(brfalse)の場合にジャンプします。あなたのベンチマークは、途中に基づいて3つの理由のために偽かもしれ(ここに掲載されていない)あなたが生成されたコードを呼び出す:

  1. を、あなたは、他の生成されたコードでそれをしない呼び出す場合(のみデリゲートでそれを呼び出すことができます)
  2. あなたの通常のコードは、代理人と比較できるように呼び出さなければなりません。
  3. 静的メソッドへの委譲は、静的メソッドの委譲ビルドよりも高速です(clrは、実際の処理の前にヌルを押してジャンプし、使用していないヌル値をポップします)。最初の未使用の引数(参照型)を使用して静的メソッドを生成し、それを防ぐためにターゲットに対してnullに明示的に指定されたターゲットでDelegate.CreateDelegateを呼び出す必要があります。
0

あなたは高速なローカル・アクセス命令が使用されている、C#コンパイルされたコードで見ることができるように:

IL_000b: box [mscorlib]System.Double 
IL_0010: stloc.0 
IL_0011: dup 
IL_0012: ldloc.0 
... 
IL_0018: ldloc.0 

代わりに、あなたのIL生成されたコードで、あなたものオペランドを取りたstloc.sldloc.sを使用しますローカルインデックス

が生成されているごとに生成されたメソッドがキャッシュされていることを確認してください。

関連する問題