Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/134.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/magento/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么认为内联比函数调用更快?_C++_Optimization_Compilation_Inline_Function Call - Fatal编程技术网

C++ 为什么认为内联比函数调用更快?

C++ 为什么认为内联比函数调用更快?,c++,optimization,compilation,inline,function-call,C++,Optimization,Compilation,Inline,Function Call,现在,我知道这是因为没有调用函数的开销,但是调用函数的开销真的有那么大吗(而且值得内联) 根据我所记得的,当调用函数时,比如f(x,y),x和y被推到堆栈上,堆栈指针跳转到一个空块,并开始执行。我知道这有点过于简单,但我是否遗漏了什么?几次推送和一次跳转来调用一个函数,真的有那么多开销吗 如果我忘了什么,请告诉我,谢谢 因为没有电话。函数代码只是被复制,因为没有执行跳转。当函数被多次调用时,内联会产生很大的不同。跳转的另一个潜在副作用是,您可能会触发页面错误,或者第一次将代码加载到内存中,或者,

现在,我知道这是因为没有调用函数的开销,但是调用函数的开销真的有那么大吗(而且值得内联)

根据我所记得的,当调用函数时,比如f(x,y),x和y被推到堆栈上,堆栈指针跳转到一个空块,并开始执行。我知道这有点过于简单,但我是否遗漏了什么?几次推送和一次跳转来调用一个函数,真的有那么多开销吗


如果我忘了什么,请告诉我,谢谢

因为没有电话。函数代码只是被复制,因为没有执行跳转。

当函数被多次调用时,内联会产生很大的不同。

跳转的另一个潜在副作用是,您可能会触发页面错误,或者第一次将代码加载到内存中,或者,如果它的使用频率不够高,以后会被从内存中调出。

没有调用和堆栈活动,这肯定会节省一些CPU周期。在现代CPU中,代码局部性也很重要:执行调用可以刷新内存,并迫使CPU等待内存被提取。这在紧密循环中非常重要,因为主内存比现代CPU慢很多

但是,如果您的代码在应用程序中只被调用了几次,则不要担心内联。如果在用户等待答案时,它被调用了数百万次,那就太令人担心了

int sum(const int &a,const int &b)
{
     return a + b;
}
int a = sum(b,c);
等于

int a = b + c

无跳转-无开销

除了没有调用(因此没有相关费用,如调用前的参数准备和调用后的清理)之外,内联还有另一个显著的优点。当函数体内联时,可以在调用方的特定上下文中重新解释它的主体。这可能会立即允许编译器进一步减少和优化代码

举个简单的例子,这个函数

void foo(bool b) {
  if (b) {
    // something
  }
  else {
    // something else
  }
}
如果作为非内联函数调用,则需要实际的分支

foo(true);
...
foo(false);
但是,如果上述调用是内联的,编译器将立即能够消除分支。本质上,在上述情况下,内联允许编译器将函数参数解释为编译时常量(如果参数是编译时常量)——这在非内联函数中通常是不可能的

然而,它甚至远不止于此。一般来说,内联所带来的优化机会意义更为深远。例如,当函数体内联到特定调用方的上下文中时,一般情况下,编译器将能够将调用代码中存在的已知别名相关关系传播到内联函数代码中,从而使优化函数代码成为可能


同样,可能的例子很多,所有这些例子都源于这样一个基本事实,即内联调用被嵌入到特定调用方的上下文中,从而实现了各种上下文间优化,这在非内联调用中是不可能的。通过内联,您基本上可以获得原始函数的许多单独版本,每个版本都是针对每个特定调用方上下文进行定制和优化的。其代价显然是代码膨胀的潜在危险,但如果正确使用,它可以提供显著的性能优势。

内联速度更快有多种原因,其中只有一个原因是显而易见的:

  • 没有跳转指令
  • 更好的本地化,从而提高缓存利用率
  • 编译器的优化器有更多机会进行优化,例如将值保留在寄存器中

缓存利用率也可能对您不利-如果内联使代码更大,则缓存未命中的可能性更大。不过,这种情况不太可能发生。

对编译器来说,内联函数是用定义替换函数调用的建议。如果它被替换,那么就不会有调用堆栈操作[push,pop]的函数。但并非总是如此。:)

--干杯

“几次推送和一次跳转来调用一个函数,真的有那么多开销吗?”

这取决于功能

如果函数体只是一条机器代码指令,那么调用和返回开销可能会高达100%。比如说,6倍,500%的开销。然后,如果您的程序只包含对该函数的数以百万计的调用,而没有内联,那么您已经将运行时间增加了500%

然而,在另一个方向上,内联可能会产生有害影响,例如,因为没有内联的代码将适合一页内存,而没有内联的代码则不适合


因此,当涉及到优化时,答案总是首先衡量的。考虑一个简单的函数,如:

int SimpleFunc (const int X, const int Y)
{
    return (X + 3 * Y); 
}    

int main(int argc, char* argv[])
{
    int Test = SimpleFunc(11, 12);
    return 0;
}
这将转换为以下代码(MSVC++v6,调试):

您可以看到,函数体只有4条指令,函数开销只有15条指令,不包括调用函数本身的另外3条指令。如果所有指令占用相同的时间(它们没有),那么80%的代码是函数开销

对于这样一个简单的函数,函数开销代码很有可能与主函数体本身一样长时间运行。当您有在深循环体中被调用数百万次/数十亿次的琐碎函数时,函数调用开销开始变大


与往常一样,关键在于分析/测量以确定内联特定函数是否会产生任何净性能增益。对于不被“经常”调用的更为“复杂”的函数,内联带来的好处可能非常小。

优化编译器应用一组启发式方法来确定内联是否有益

有时,从缺少函数调用中获得的收益将超过
10:   int SimpleFunc (const int X, const int Y)
11:   {
00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,40h
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-40h]
0040102C   mov         ecx,10h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]

12:       return (X + 3 * Y);
00401038   mov         eax,dword ptr [ebp+0Ch]
0040103B   imul        eax,eax,3
0040103E   mov         ecx,dword ptr [ebp+8]
00401041   add         eax,ecx

13:   }
00401043   pop         edi
00401044   pop         esi
00401045   pop         ebx
00401046   mov         esp,ebp
00401048   pop         ebp
00401049   ret
int getData()
{
   return data ;
}