Why is casting for a generic type slower than explicit listing in C #?

I create a posting map in C # and basically just play with different approaches. I am interested to know about the difference in performance that I am measuring, but it is not obvious why looking at IL.

Message Card:

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; 

Then I have a message class hierarchy similar to EventArgs in WPF, for example:

 public class Message {} public class VelocityUpdateMessage : Message 

and observer classes with handler functions:

 void HandleVelocityUpdate(VelocityUpdateMessage message) { ... } 

I am measuring 2 ways to add and call handlers. I am wrapping a delegate call, so I can get a little conceptual type of security, and that is the difference in level.

Approach 1: Listener Calls

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

Approach 2: Listener Calls

 AddHandler<VelocityUpdateMessage>(HandleVelocityUpdate); 

Both approaches create a MessageHandler delegate that makes the cast the same method call, but calling delegates built using approach # 2 is slower, although the generated IL looks the same. Is this extra redundant runtime when casting to a generic type? Is this a type restriction? I would expect JITted delegates to be the same once the generic type is resolved.

Thanks for any info.

+6
source share
2 answers

ok, I had to look for MethodBody.GetILAsByteArray () IL and not ILSpy results for delegates to figure this out. Using a common delegate to port my message handler and create a message type generates:

 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 

where a shell delegate with an explicit expression generates:

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

So yes, there is minimal overhead from using generics this way.

0
source

The next line creates a new instance of the anonymous type with each call. Could the cause of the difference in performance?

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

Source: https://habr.com/ru/post/906529/


All Articles