Type Comparison Operating Costs

I am decoding comms messages from a binary stream. I create message objects of various types depending on which messages arrived. All of them are manufactured from the base type CommsMessage . All beautiful and dandy.

Elsewhere in my code, I need to respond to these messages, so I need to know what the message is.

I am currently doing:

 void ProcessIncomingMessage(CommsMessage msg) { if (msg is MessageType1) return ProcessMessageType1(msg as MessageType1); if (msg is MessageType2) return ProcessMessageType2(msg as MessageType2); //etc } 

I am wondering what the execution cost is for comparing these types, and should I include the MessageType property in the base class. Then I could do:

 void ProcessIncomingMessage(CommsMessage msg) { switch (msg.MessageType) { case MessageType.Type1: return ProcessMessageType1(msg as MessageType1); case MessageType.Type2: return ProcessMessageType2(msg as MessageType2); //etc } } 

Yes, this is a premature optimization, and I'm probably worried about minor details, but I'm such an encoder who likes to know what is happening under the covers, and therefore was interested in the differences in performance between them. I assume that I have a bias against type mappings with my C ++ background, where RTTI introduced overhead and just wondered if .NET had any similarities.

+6
source share
3 answers

Do you think you are deleting a type?

I assume that you thought that including a virtual method in the Message type would break the layer abstraction (for example, you might need a clean separation of message processing from the message itself). Perhaps consider a template. This will allow you to separate the Message class from the Message processing itself.

If you have any of this structure.

 abstract class CommsMessage {} class Message1 : CommsMessage {} class Message2 : CommsMessage {} 

You can reorganize

 abstract class CommsMessage { public abstract void Visit(CommsMessageVisitor v); } class Message1 : CommsMessage { public void Visit(CommsMessageVisitor v) { v.Accept(this); } } class Message2 : CommsMessage { public void Visit(CommsMessageVisitor v) { v.Accept(this); } } interface CommsMessageVisitor { void Accept(Message1 msg1); void Accept(Message1 msg2); } 

At this point, you excluded tip-throws. Now you can rewrite your code as

 void ProcessIncomingMessage(CommsMessage msg) { new MyVisitor().Visit(msg); } class MyVisitor : CommsMessageVisitor { void Accept(Message1 msg1) { ProcessMessageType1(msg1); } void Accept(Message1 msg2) { ProcessMessageType2(msg2); } } 

Of course, there may be reasons why you cannot do this, but it is always better to avoid typecasting if you can!

+7
source

Please note that your code is not syntactically valid, since the return types are void , but anyway.

Well, I'm not too sure about the differences in performance between the two alternatives you are showing. However, at least FxCop β€œ suggest ” instead of your first solution:

 void ProcessIncomingMessage(CommsMessage msg) { MessageType1 msg1 = msg as MessageType1; if (msg1 != null) { ProcessMessageType1(msg1); return; } MessageType2 msg2 = msg as MessageType2; if (msg2 != null) { ProcessMessageType2(msg2); return; } //etc } 

Of course, there are other problems here, such as maintainability, understandability, etc. It might be better to provide a "virtual void ProcessMessage ()" in your class "CommsMessage", which you rewrite for each "MessageType". Then let the CLR work for you.

 public class CommsMessage { public virtual void ProcessMessage() { // Common stuff. } } public class MessageType1 : CommsMessage { public override void ProcessMessage() { base.ProcessMessage(); // type 1 specific stuff. } } // ... void ProcessIncomingMessage(CommsMessage msg) { msg.ProcessMessage(); } 

Perhaps you can directly call msg.ProcessMessage() , where you now call ProcessIncomingMessage if there is nothing to do.

+2
source

To add to the great answers above:

In performance profiling, I noticed that using is followed by as actually resulted in lower performance than a single as followed by a zero check. Do not expect the compiler to automatically optimize anything. You are right to assume that in messaging code (or in other mission-critical environments) that design for speed is of utmost importance.

Today, the fastest cast is static casting, which is superior to as , i.e. var message = (SpecificType)baseMessage will exceed var message = baseMessage as SpecificType . This is an interesting topic only if a static cast does not help you in your case.

As the two answers have already been mentioned, doing the above in a polymorphic way using a design pattern may be the best solution since it only adds a call to the virtual method. Retrieving a common method for an abstract class (or a common method signature for an interface) is by far the most elegant solution. There is overhead for invoking a virtual method, but this can be mitigated by noting specific methods on derived types using the sealed .

Finally, use generators, where possible, to eliminate castings, since common methods are optimizing compilation time rather than casting at run time.

Respectfully,

+1
source

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


All Articles