C++ 为什么即使函数是内联的,代码也会变慢?

C++ 为什么即使函数是内联的,代码也会变慢?,c++,performance,gcc,refactoring,inline,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个),

我有这样一种方法:

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个),并且函数变得越来越大,所以我尝试提取case1中的代码,并将其放入一个内联函数中,如下所示:

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)

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

这很难找到。一个问题可能是代码膨胀,导致大部分循环被推出(小)CPU缓存。。。但现在我想起来,这也不完全有道理

我建议做什么:

在仍然能够观察到减速的情况下,尽可能隔离代码和条件


然后,去分析它。这种分析有意义吗?现在,(假设你已经准备好冒险)分解代码,看看g++有什么不同。在这里报告这些结果

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

如果没有看到ComplexCondition部分,很难说。如果该条件足够复杂,编译器将无法正确地对其进行管道传输,这将干扰芯片中的分支预测。只是一种可能性。

汇编程序是否告诉您发生了什么?虽然我大体上赞同iaimtomisbehave的jmp思想,但查看反汇编可能比让我们猜测更容易。植入断点,进入“反汇编”视图,然后开始单步执行


所有的谜团都会消失。

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

  • 声明顺序。编译器将此语句及其复杂代码放在最后是有意义的。这意味着其他案例将首先进行评估,除非必要,否则不会进行检查。如果简化语句,它可能不会这样做,这意味着您的疯狂条件每次都会得到完全计算

  • 创建额外的案例。在某些情况下,应该可以从if语句中提取一些coditional,并生成一个额外的case语句。这可以消除一些检查

  • 流水线失败了。即使它内联,它也无法分解实际内联中的代码。这是所有这三种方法的基本问题,但对于管道,这显然会导致问题,因为对于管道,您希望在进入检查本身之前开始执行


  • 您的
    案例
    块没有
    中断
    ——这是故意的吗?@GMan:我比较了更改前后整个应用程序的执行时间。更改后,整个应用程序的速度降低了2.5%(是的,在这种情况下,2.5%是一个大问题)。@OP:在实时中,2.5%是多少?时钟测量时间有多精确?@Earlz:整个应用程序的执行时间增加了2.5%。我没有单独测试这个函数,但是因为它只是内部循环的一小部分,我猜它要慢得多。无论如何,对我来说,问题是我不明白为什么编译器不能为这两个版本生成等效的输出。我想应该问:您的优化级别是什么<代码>-03?看我的帖子。我验证并确认该函数是由gcc内联的。事实上,我只是想确保他知道这一点。我和马修一样困惑,为什么演出会改变;编译器应该能够生成相同的代码。当我发布关于此问题的抱歉时,没有看到注释。是的,条件是复杂的,并且意味着调用其他函数(在某些情况下),但是ComplexCondition在两个版本的代码中是相同的,所以我认为它会产生相同的影响。我说的对吗?取决于你使用的编译器。GCC在内联之前进行指令调度,而VisualStudio在IIRC之后进行指令调度。好建议。我这样做了(见我的编辑的OP),但它没有帮助,可能是因为我没有很好地理解我所看到的。或者我没有正确使用gdb。