即使函数内联,为什么这段代码会变慢?

时间:2010-02-02 20:49:46

标签: c++ performance gcc refactoring inline

我有这样的方法:

bool MyFunction(int& i)
{
  switch(m_step)
  {
    case 1:
       if (AComplexCondition)
       {
         i = m_i;
         return true;
       }

    case 2:
      // some code

    case 3:
      // some code
  }
}

由于有很多case语句(超过3个)并且函数变得很大,我试图在case 1中提取代码并将它放在这样的内联函数中:

inline bool funct(int& i)
{
  if (AComplexCondition)
  {
    i = m_i;
    return true;
  }
  return false;
}
bool MyFunction(int& i)
{
  switch(m_step)
  {
    case 1:
       if (funct(i))
       {
         return true;
       }

    case 2:
      // some code

    case 3:
      // some code
  }
}

看起来这段代码比原来慢得多。 我使用-Winline检查并且函数是内联的。为什么这段代码会变慢?我认为这是等价的。我看到的唯一区别是在第二个版本中还有一个条件检查,但我认为编译器应该能够优化它。正确?

修改 有些人建议我应该使用gdb来停止两个版本中的每个汇编指令以查看差异。我做到了。

第一个版本看起来像这样:

mov
callq (Call to AComplexCondition())
test
je (doesn't jump)
mov (i = m_i)
movl (m_step = 1)

第二个版本,有点慢似乎更简单。

movl (m_step = 1)
callq (Call to AComplexCondition())
test
je (doesn't jump)
mov (i = m_i)
xchg %ax,%ax (This is a nop I think)

这两个版本似乎做同样的事情,所以我仍然不知道为什么第二个版本仍然较慢。

6 个答案:

答案 0 :(得分:3)

只需单步执行即可。设置一个断点,进入反汇编视图,然后开始踩踏。

所有的奥秘都会消失。

答案 1 :(得分:2)

这很难追查。一个问题可能是代码膨胀导致大部分循环被推出(小)CPU缓存......但是现在我想起来并不完全有意义。

我建议做什么:

尽可能地隔离代码和条件,同时仍然能够观察到减速。

然后,去剖析它。分析是否有意义?现在,(假设你的冒险)取消了代码的分配,看看g ++在做什么不同。将这些结果报告回来

答案 2 :(得分:1)

GMan是正确的,内联并不保证您的函数将被内联。这是编译器的一个提示,它可能是一个好主意。如果编译器认为内联函数并不明智,那么现在就有了函数调用的开销。这至少意味着正在执行两个JMP声明。这意味着函数的指令存储在非顺序位置,而不是存储在调用函数的下一个内存位置,执行将移动新位置完成它并在函数调用后返回。

答案 3 :(得分:1)

没有看到ComplexCondition部分,很难说。如果该条件足够复杂,编译器将无法正确地对其进行流水线操作,并且它将干扰芯片中的分支预测。只是一种可能性。

答案 4 :(得分:1)

汇编程序是否告诉您有关正在发生的事情的事情?虽然我一般都认同iaimtomisbehave的jmp想法,但看看反汇编可能比让我们猜测更容易。

答案 5 :(得分:1)

这是一个很好的问题。让我们知道你发现了什么。我确实有一些想法,主要源于编译器不再能够分解您内联的代码,但没有保证答案。

  1. 声明顺序。有意义的是,编译器会将此语句的复杂代码放在最后。这意味着将首先评估其他情况,除非必要,否则永远不会进行检查。如果你简化语句,它可能不会这样做,这意味着每次都会对你的疯狂条件进行全面评估。

  2. 创建额外案例。应该可以从if语句中提取一些代码,并在某些情况下做一个额外的案例。这可能会消除一些检查。

  3. 流水线打败了。即使它内联,它也无法分解实际内联中的代码。这是所有这三个问题的基本问题,但是使用流水线操作会导致问题,因为对于流水线操作,您希望在进行检查之前开始执行。