2012-01-21 18 views
6

私はC#でメッセージディスパッチマップを構築しています。私は測定しているパフォーマンスの違いについて好奇心が強いですが、なぜILを調べるのは明らかではありません。C#の明示的なキャストよりもジェネリック型のキャストが遅いのはなぜですか?

メッセージマップ:

delegate void MessageHandler(Message message); 
AddHandler(Type t, MessageHandler handler) 
{ 
    /* add 'handler' to messageMap invocation list */ 
} 

delegate void GenericMessageHandler<T>(T message); 
AddHandler<T>(GenericMessageHandler<T> handler) where T: Message 
{ 
    AddHandler(typeof(T), e => { handler((T)e); }); 
} 

Dictionary<Type, MessageHandler> messageMap; 

Iは、次いで、例えば、WPFでのEventArgsと同様のメッセージのクラス階層を有する:

public class Message {} 
public class VelocityUpdateMessage : Message 

オブザーバハンドラ関数を持つクラス:

void HandleVelocityUpdate(VelocityUpdateMessage message) { ... } 

&ハンドラーを呼び出す2つの方法を測定しています。私はデリゲートコールをラッピングしているので、概念的な型の安全性を少し得ることができ、そこにperfの違いがあります。

アプローチ1:リスナーが呼び出されます

AddHandler(typeof(VelocityUpdateMessage), 
      e => { HandleVelocityUpdate((VelocityUpdateMessage)e); }); 

アプローチ2:リスナーは

AddHandler<VelocityUpdateMessage>(HandleVelocityUpdate); 

は、両方のアプローチは、キャ​​ストと同じメソッドの呼び出しを行うMessageHandlerのデリゲートを構築呼び出しますが、呼び出すデリゲートが使用して構築アプローチ#2は、生成されたILが同一に見えても、少し遅い。ジェネリック型へのキャスト時に余分なランタイムオーバーヘッドですか?型制約ですか?ジェネリック型が解決されたら、JITtedの代理人は同じになると思います。

ありがとうございました。

+2

どのように測定していますか?これは、これらのマイクロ最適化では非常に重要です。 –

答えて

0

[OK]を、私はこれの一番下に到達するためにMethodBody.GetILAsByteArray()ILはなく代表者のためのILSpy結果を見ていました。私のメッセージハンドラをラップするために、汎用デリゲートを使用して、メッセージタイプをキャストして生成します。

明示的なキャストを持つラッパー委譲が発生
0000 : ldarg.0 
0001 : ldfld 
0006 : ldarg.1 
0007 : unbox.any 
000C : callvirt void MessageTest.Message+tMessageHandler`1[MessageTest.VelocityUpdateMessage].Invoke(‌​MessageTest.VelocityUpdateMessage) 
0011 : ret 

0000 : ldarg.0 
0001 : ldarg.1 
0002 : castclass 
0007 : call void Message.Component.HandleVelocityUpdate(MessageTest.VelocityUpdateMessage) 
000C : ret 

そうです、ジェネリックを使用してから最小のオーバーヘッドがありますこの方法では。

3

以下の行は、呼び出されるたびに匿名型の新しいインスタンスを作成します。あなたのパフォーマンスの違いの原因は?

AddHandler(typeof(T), e => { handler((T)e); }); 
+0

行に 'new {...}'が含まれていません。匿名タイプはどこですか? – dtb

+0

明確にするために、(ハンドラにメッセージをディスパッチする)デリゲートを呼び出すときのパフォーマンスの違いがわかりました。セットアップコードなので、AddHandlerの呼び出しをプロファイリングしていません。クリストファー氏によると、私は新しいメソッドを各タイプごとに用意するつもりですが、テストコードでは厳密なループで何度も同じハンドラーを繰り返し呼び出しているだけなので、コード生成のヒットはちょっとしたものにすべきです。多分私はそれ以上と思っています。私は#1に対して100kコールに対して〜70msの経過時間を、#2に対して〜110msの経過時間を見ている。 Delegate.DynamicInvokeを呼び出すのと同じくらいのオーダーではありません。 –

+0

少なくとも匿名型ではありません。少なくとも、正式なC#言語定義ではありません。ラムダ式を動作させる隠しクラスがあり、* new *式も隠されています。しかし、OPは逆の観察をする。 –

関連する問題