这些代码示例中的哪一个具有更好的性能?

问题描述:

在对one of my questions的回复中,我收到了很多答案,说样式2可能比样式1更好。我不明白怎么样,因为我相信他们应该发出基本相同的机器指令(如果使用C++编写的话)。你能解释为什么风格2可能表现更好吗?这些代码示例中的哪一个具有更好的性能?

我会在这里改写了两种风格,方便参考:

样式1

while (!String.IsNullOrEmpty(msg = reader.readMsg())) 
{ 
    RaiseMessageReceived(); 
    if (parseMsg) 
    { 
     ParsedMsg parsedMsg = parser.parseMsg(msg); 
     RaiseMessageParsed(); 
     if (processMsg) 
     { 
      process(parsedMsg); 
      RaiseMessageProcessed(); 
     } 
    } 
} 

款式二:

while (!String.IsNullOrEmpty(msg = reader.readMsg())) 
{ 
    RaiseMessageReceived(); 
    if (!parseMsg) continue; 

    ParsedMsg parsedMsg = parser.parseMsg(msg); 
    RaiseMessageParsed(); 
    if (!processMsg) continue; 

    process(parsedMsg); 
    RaiseMessageProcessed(); 
} 

我不得不检查。

这里是我的代码版本:

using System; 
using System.Collections.Generic; 

namespace ConsoleApplication2 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Tester t=new Tester(); 
      t.Method1(new Stack<string>(), new MsgParser(), true, true); 
      t.Method2(new Stack<string>(), new MsgParser(), true, true); 
     } 
    } 
    class Tester 
    { 
     public void Method1(Stack<string> strings, MsgParser parser, bool parseMsg, bool processMsg) 
     { 
      string msg; 
      while (!String.IsNullOrEmpty(msg = strings.Pop())) 
      { 
       RaiseMessageReceived(); 
       if (parseMsg) 
       { 
        ParsedMsg parsedMsg = parser.ParseMsg(msg); 
        RaiseMessageParsed(); 
        if (processMsg) 
        { 
         process(parsedMsg); 
         RaiseMessageProcessed(); 
        } 
       } 
      } 
     } 

     public void Method2(Stack<string> strings, MsgParser parser, bool parseMsg, bool processMsg) 
     { 
      string msg; 
      while (!String.IsNullOrEmpty(msg = strings.Pop())) 
      { 
       RaiseMessageReceived(); 
       if (!parseMsg) continue; 

       ParsedMsg parsedMsg = parser.ParseMsg(msg); 
       RaiseMessageParsed(); 
       if (!processMsg) continue; 

       process(parsedMsg); 
       RaiseMessageProcessed(); 
      } 

     } 

     private void RaiseMessageProcessed() 
     { 
      Console.WriteLine("Done"); 
     } 

     private void process(ParsedMsg msg) 
     { 
      Console.WriteLine(msg); 
     } 

     private void RaiseMessageParsed() 
     { 
      Console.WriteLine("Message parsed"); 
     } 

     private void RaiseMessageReceived() 
     { 
      Console.WriteLine("Message received."); 
     } 
    } 

    internal class ParsedMsg 
    { 
    } 

    internal class MsgParser 
    { 
     public ParsedMsg ParseMsg(string msg) 
     { 
      return new ParsedMsg(); 
     } 
    } 
} 

我用代码优化(默认发布配置)建立了它,并使用反射拆卸组装。 结果验证了这两种风格是相同的:

internal class Tester 
{ 
    // Methods 
    public void Method1(Stack<string> strings, MsgParser parser, bool parseMsg, bool processMsg) 
    { 
     string msg; 
     while (!string.IsNullOrEmpty(msg = strings.Pop())) 
     { 
      this.RaiseMessageReceived(); 
      if (parseMsg) 
      { 
       ParsedMsg parsedMsg = parser.ParseMsg(msg); 
       this.RaiseMessageParsed(); 
       if (processMsg) 
       { 
        this.process(parsedMsg); 
        this.RaiseMessageProcessed(); 
       } 
      } 
     } 
    } 

    public void Method2(Stack<string> strings, MsgParser parser, bool parseMsg, bool processMsg) 
    { 
     string msg; 
     while (!string.IsNullOrEmpty(msg = strings.Pop())) 
     { 
      this.RaiseMessageReceived(); 
      if (parseMsg) 
      { 
       ParsedMsg parsedMsg = parser.ParseMsg(msg); 
       this.RaiseMessageParsed(); 
       if (processMsg) 
       { 
        this.process(parsedMsg); 
        this.RaiseMessageProcessed(); 
       } 
      } 
     } 
    } 

    private void process(ParsedMsg msg) 
    { 
     Console.WriteLine(msg); 
    } 

    private void RaiseMessageParsed() 
    { 
     Console.WriteLine("Message parsed"); 
    } 

    private void RaiseMessageProcessed() 
    { 
     Console.WriteLine("Done"); 
    } 

    private void RaiseMessageReceived() 
    { 
     Console.WriteLine("Message received."); 
    } 
} 

性能应该是相同的,无论是谁否则一定会......困惑。

最好的答案是查看生成的字节码/程序集并查看。然后忽略你所看到的,因为优化的JIT编译器会根据对执行代码的实时分析来改变它。所以坚持最能表达意图的风格。

也就是说,风格2应该直接跳回到条件,而风格1可以想象跳过if块只是再次跳转到条件。

这不得不过早地优化我所见过的最好例子。

+0

其实这个问题既不是关于风格,也没有优化。风格在我之前的问题中已经讨论过了,我知道在这种情况下优化是无关紧要的。但是我想知道编译器/ JIT的实际输出。 – 2009-01-01 08:48:58

+0

您可能没有明确地这么说,但是当您询问A是否会比B更好地执行时,您正在询问优化问题。哪两种类似替代方案的性能最佳? – 2009-01-02 07:05:26

为什么不从杰夫的书中抽出一小段代码,如this question

+0

我的问题具有更多的理论性质,所以时间不是我正在寻找的。我担心的是,如果代码是用C++编写的,那么这个代码会(在理论上)被编译到相同的程序集中,所以我担心它是否与C#不一样? – 2009-01-01 08:44:36

+0

@Hosma Aly,我不明白。你想知道什么效果更好,但时间不是你想要的? – tuinstoel 2009-01-01 13:39:19

我认为如果性能完全不同,性能可以忽略不计。无论如何,编译器可能会将它们优化为相同的形式。

唯一的实质性差异是文体。

我喜欢style 1只是因为循环有一个入口点(每次迭代)和一个出口点(每次迭代),所以很容易在循环结尾插入调试代码并知道它会被调用。在一个函数的入口和出口点背后是相同的原理(出于同样的原因)。尽管如此,缩进可能很难阅读,所以继续也有它的位置。

代码流看起来是相同的,字节码应该是相同的。

免责声明:我是一个C/C++程序员,我不会做C#