【问题标题】:Performance cost of type comparisons类型比较的性能成本
【发布时间】:2012-02-14 21:11:45
【问题描述】:

我正在从二进制流中解码通信消息。我根据到达的消息创建不同类型的消息对象。它们都派生自基本CommsMessage 类型。一切都很好。

在我的代码的其他地方,我需要对这些消息做出反应,所以我需要知道它是什么类型的消息。

目前我在做:

void ProcessIncomingMessage(CommsMessage msg)
{
    if (msg is MessageType1)
        return ProcessMessageType1(msg as MessageType1);

    if (msg is MessageType2)
        return ProcessMessageType2(msg as MessageType2);

    //etc
}

我想知道比较这些类型的性能成本是多少,以及是否应该在基类中包含 MessageType 属性。然后我可以这样做:

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

         //etc
    }
}

是的,这是过早的优化,我可能会担心无关紧要的细节,但我是那种喜欢了解幕后情况的编码人员,因此想知道两者之间的性能差异。我想我对 C++ 背景的类型比较有偏见,因为 RTTI 引入了开销,只是想知道 .Net 是否有任何相似之处。

【问题讨论】:

标签: c# performance types


【解决方案1】:

添加到上面的优秀答案:

在性能分析中,我注意到使用is 后跟as 实际上导致性能低于单个as 后跟空检查。不要指望编译器会自动优化任何东西。您正确地假设在消息传递代码(或其他性能关键部分)中,为速度而设计是至关重要的。

到目前为止,最快的演员是静态演员,它优于as,即var message = (SpecificType)baseMessage将优于var message = baseMessage as SpecificType。这只是一个有趣的点,因为静态演员不能帮助您解决您的问题。

正如两个答案已经提到的那样,使用设计模式以多态方式执行上述操作可能是最好的解决方案,因为它只添加了一个虚拟方法调用。将通用方法提取到抽象类(或将通用方法签名提取到接口)是迄今为止最优雅的解决方案。调用虚方法会产生开销,但这可以通过使用 sealed 关键字在派生类型上标记特定方法来减轻。

最后,尽可能使用泛型来消除强制转换,因为泛型方法是一种编译时优化,而不是运行时强制转换。

最好的问候,

【讨论】:

    【解决方案2】:

    请注意,您的代码在语法上无效,因为返回类型是 void,但无论如何。

    好吧,我不太确定您展示的两种替代方案的性能差异。但是,至少 FxCop 会“suggest”而不是您的第一个解决方案:

    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
    }
    

    当然,这里还涉及其他问题,例如可维护性、可理解性等。 可能最好在“CommsMessage”类上提供一个“virtual void ProcessMessage()”,为每个“MessageType”覆盖它。然后让 CLR 为您工作。

    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();
    }
    

    可以说,您可以直接拨打msg.ProcessMessage(),如果没有其他事情可做,您现在可以拨打ProcessIncomingMessage

    【讨论】:

    • +1 来自我 - as 后跟 null 检查优于 is 然后 as
    【解决方案3】:

    您是否考虑过消除类型转换?

    我猜您已经考虑过将虚拟方法放在 Message 类型本身上会破坏分层抽象(例如,您可能希望将消息处理与消息本身完全分离)。也许考虑visitor pattern。这将允许您将Message 类与Message 本身的处理分开。

    如果你有这种结构。

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

    你可以重构为

    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);
    }
    

    此时,您已经消除了类型转换。您现在可以将代码重写为

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

    当然,你不能这样做可能是有原因的,但如果可以的话,最好避免类型转换!

    【讨论】:

    • +1 供您解释/假设为什么游客在这里可能是必要的 - 否则它似乎有点矫枉过正;-)
    • 我同意,除非是这样,否则绝对是矫枉过正!不过,这似乎是一个相当普遍的情况。您有一些愚蠢的对象(PO​​CO / 实体),您只想将它​​们转移到各个地方(可能在客户端/服务器之间共享,或者只是在不同的抽象级别上共享)。你经常想要对它进行不同的处理,但是将它推到虚拟方法中会暴露太多。访问者模式非常适合移除这些方法的实现。
    • 是的,鉴于所涉及的类型和方法的名称,您的假设是合理的。
    • 一个正确的假设。这是完美的,谢谢!整个类型比较对我来说感觉不对,但我看不出有一种干净的方法来区分它们。是时候读完我认为的设计模式了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-09
    • 1970-01-01
    • 2015-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多