我有这样的方法:
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)
这两个版本似乎做同样的事情,所以我仍然不知道为什么第二个版本仍然较慢。
答案 0 :(得分:3)
只需单步执行即可。设置一个断点,进入反汇编视图,然后开始踩踏。
所有的奥秘都会消失。
答案 1 :(得分:2)
这很难追查。一个问题可能是代码膨胀导致大部分循环被推出(小)CPU缓存......但是现在我想起来并不完全有意义。
我建议做什么:
尽可能地隔离代码和条件,同时仍然能够观察到减速。
然后,去剖析它。分析是否有意义?现在,(假设你的冒险)取消了代码的分配,看看g ++在做什么不同。将这些结果报告回来
答案 2 :(得分:1)
GMan是正确的,内联并不保证您的函数将被内联。这是编译器的一个提示,它可能是一个好主意。如果编译器认为内联函数并不明智,那么现在就有了函数调用的开销。这至少意味着正在执行两个JMP声明。这意味着函数的指令存储在非顺序位置,而不是存储在调用函数的下一个内存位置,执行将移动新位置完成它并在函数调用后返回。
答案 3 :(得分:1)
没有看到ComplexCondition部分,很难说。如果该条件足够复杂,编译器将无法正确地对其进行流水线操作,并且它将干扰芯片中的分支预测。只是一种可能性。
答案 4 :(得分:1)
汇编程序是否告诉您有关正在发生的事情的事情?虽然我一般都认同iaimtomisbehave的jmp想法,但看看反汇编可能比让我们猜测更容易。
答案 5 :(得分:1)
这是一个很好的问题。让我们知道你发现了什么。我确实有一些想法,主要源于编译器不再能够分解您内联的代码,但没有保证答案。
声明顺序。有意义的是,编译器会将此语句的复杂代码放在最后。这意味着将首先评估其他情况,除非必要,否则永远不会进行检查。如果你简化语句,它可能不会这样做,这意味着每次都会对你的疯狂条件进行全面评估。
创建额外案例。应该可以从if语句中提取一些代码,并在某些情况下做一个额外的案例。这可能会消除一些检查。
流水线打败了。即使它内联,它也无法分解实际内联中的代码。这是所有这三个问题的基本问题,但是使用流水线操作会导致问题,因为对于流水线操作,您希望在进行检查之前开始执行。