6

私はC#でメッセージディスパッチマップを作成しており、ほとんどの場合、いくつかの異なるアプローチで遊んでいます。測定しているパフォーマンスの違いに興味がありますが、ILを見てなぜかは明らかではありません。

メッセージマップ:

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;

次に、WPFのEventArgsに似た、メッセージのクラス階層があります。次に例を示します。

public class Message {}
public class VelocityUpdateMessage : Message

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

void HandleVelocityUpdate(VelocityUpdateMessage message) { ... }

ハンドラーを追加および呼び出す2つの方法を測定しています。デリゲートコールをラップしているので、概念的なタイプの安全性を少し得ることができ、そこにパフォーマンスの違いがあります。

アプローチ1:リスナーの呼び出し

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

アプローチ2:リスナーの呼び出し

AddHandler<VelocityUpdateMessage>(HandleVelocityUpdate);

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

情報をありがとう。

4

2 に答える 2

3

次の行は、呼び出されるたびに匿名タイプの新しいインスタンスを作成します。それがあなたのパフォーマンスの違いの原因でしょうか?

AddHandler(typeof(T), e => { handler((T)e); }); 
于 2012-01-21T08:54:27.273 に答える
0

わかりました。デリゲートがこれの最後に到達するには、ILSpyの結果ではなくMethodBody.GetILAsByteArray()ILを確認する必要がありました。ジェネリックデリゲートを使用してメッセージハンドラーをラップし、メッセージタイプをキャストすると、次のようになります。

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

そうです、このようにジェネリックを使用することによるオーバーヘッドは最小限です。

于 2012-01-21T21:25:53.607 に答える