2012-01-21 25 views
6

Tôi đang xây dựng bản đồ gửi thư trong C# và chủ yếu chỉ chơi xung quanh với một số cách tiếp cận khác nhau. Tôi tò mò về một sự khác biệt về hiệu suất mà tôi đang đo lường, nhưng không rõ tại sao lại nhìn vào IL.Tại sao truyền sang loại chung chậm hơn so với dàn diễn viên rõ ràng trong C#?

Bản đồ thông điệp:

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; 

sau đó tôi có một hệ thống phân cấp lớp của tin nhắn, tương tự như EventArgs trong WPF, ví dụ:

public class Message {} 
public class VelocityUpdateMessage : Message 

và quan sát lớp học với chức năng xử lý:

void HandleVelocityUpdate(VelocityUpdateMessage message) { ... } 

Tôi đang đo 2 cách để thêm & gọi trình xử lý. Tôi đang gói các cuộc gọi đại biểu để tôi có thể nhận được một chút về an toàn loại khái niệm và trong đó nằm sự khác biệt perf.

Tiếp cận 1: nghe gọi

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

Phương pháp 2: nghe gọi

AddHandler<VelocityUpdateMessage>(HandleVelocityUpdate); 

Cả hai phương pháp xây dựng một đại biểu MessageHandler mà làm cho một dàn diễn viên và cuộc gọi cùng một phương pháp, nhưng kêu gọi các đại biểu được xây dựng sử dụng Cách tiếp cận # 2 là một chút chậm hơn mặc dù IL được tạo ra trông giống hệt nhau. Có thêm thời gian chạy trong quá trình truyền sang loại chung không? Nó có phải là loại ràng buộc không? Tôi hy vọng các đại biểu JITted sẽ giống nhau khi loại chung được giải quyết.

Cảm ơn mọi thông tin.

+2

Bạn đo lường như thế nào? Điều đó cực kỳ quan trọng với những tối ưu hóa vi mô này. –

Trả lời

0

ok, tôi đã phải xem xét MethodBody.GetILAsByteArray() IL thay vì kết quả ILSpy cho các đại biểu để đến cuối phần này. Sử dụng một đại biểu tổng quát để quấn xử lý thông điệp của tôi và đúc các loại thông điệp tạo:

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 

nơi các đại biểu wrapper với các diễn viên rõ ràng tạo:

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

Vì vậy, có, có chi phí tối thiểu từ việc sử dụng Generics theo cách này.

3

Dòng dưới đây tạo ra một thể hiện mới của một loại ẩn danh mỗi khi nó được gọi. Có thể đó là nguyên nhân của sự khác biệt hiệu suất của bạn?

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

Dòng không chứa 'mới {...}'. Kiểu ẩn danh ở đâu? – dtb

+0

Để làm rõ, tôi thấy sự khác biệt hiệu suất khi gọi các đại biểu (gửi tin nhắn đến trình xử lý), tôi không lược tả các cuộc gọi AddHandler vì chúng là mã thiết lập. Như Christopher nói tôi sẽ nhận được một phương pháp mới được viết cho từng loại nhưng mã thử nghiệm của tôi chỉ gọi cùng một trình xử lý lặp đi lặp lại trong một vòng lặp chặt chẽ, vì vậy cú đánh cho việc tạo mã nên chỉ là một đốm nhỏ. Có lẽ nó hơn tôi nghĩ. Tôi thấy thời gian trôi qua là ~ 70ms cho 100k cuộc gọi # 1 so với ~ 110ms đối với # 2. Nó không phải là đơn đặt hàng của cường độ như gọi Delegate.DynamicInvoke. –

+0

Nó không phải là một loại vô danh, ít nhất là không có định nghĩa ngôn ngữ C# chính thức của một. Có một lớp ẩn làm cho biểu thức lambda hoạt động, biểu thức * mới * cũng bị ẩn. OP làm cho các quan sát ngược lại mặc dù. –

Các vấn đề liên quan