Почему приведение к универсальному типу происходит медленнее, чем явное приведение в C #? - PullRequest
6 голосов
/ 21 января 2012

Я создаю карту рассылки сообщений в 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;

У меня тогда есть иерархия классов сообщений,похож на EventArgs в WPF, например:

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-делегаты будут такими же, как только будет разрешен общий тип.

Спасибо за любую информацию.

Ответы [ 2 ]

3 голосов
/ 21 января 2012

Следующая строка создает новый экземпляр анонимного типа при каждом его вызове.Может ли это быть причиной вашей разницы в производительности?

AddHandler(typeof(T), e => { handler((T)e); }); 
0 голосов
/ 22 января 2012

хорошо, мне пришлось взглянуть на метод 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

Так что да, использование дженериков таким образом минимально.

...