C++ 关于C++;内联函数

C++ 关于C++;内联函数,c++,inline,C++,Inline,我参加的课程中的培训材料似乎有两种相互矛盾的说法 一方面: “使用内联函数通常会加快执行速度” 另一方面: “内联函数的使用可能会由于频繁使用而降低性能 交换“ 问题1:这两种说法都正确吗 问题2:这里的“交换”是什么意思 请浏览以下片段: int powA(int a, int b) { return (a + b)*(a + b) ; } inline int powB(int a, int b) { return (a + b)*(a + b) ; } int main ()

我参加的课程中的培训材料似乎有两种相互矛盾的说法

一方面:

“使用内联函数通常会加快执行速度”

另一方面:

“内联函数的使用可能会由于频繁使用而降低性能 交换“

问题1:这两种说法都正确吗

问题2:这里的“交换”是什么意思

请浏览以下片段:

int powA(int a, int b) {
  return (a + b)*(a + b) ;
}

inline int powB(int a, int b) {
  return (a + b)*(a + b) ;
}

int main () {
    Timer *t = new Timer;

    for(int a = 0; a < 9000; ++a) {
        for(int b = 0; b < 9000; ++b) {
             int i = (a + b)*(a + b);       //              322 ms   <-----
            //  int i = powA(a, b);         // not inline : 450 ms
            //  int i = powB(a, b);         // inline :     469 ms
        }
    }

    double d = t->ms();
    cout << "-->  " << d << endl; 

    return 0;
}
intpowa(inta,intb){
回报(a+b)*(a+b);
}
内联int powB(int a,int b){
回报(a+b)*(a+b);
}
int main(){
定时器*t=新定时器;
对于(int a=0;a<9000;++a){
对于(int b=0;b<9000;++b){
int i=(a+b)*(a+b);//322 ms();
问题1
是的,在特定情况下,两种说法都可能是正确的。显然,它们不会同时都是正确的

问题2 “交换”很可能是指操作系统分页行为,即当内存压力变大时,页面被交换到磁盘


在实践中,如果内联函数很小,则通常会注意到由于消除了函数调用和返回的开销而带来的性能改进。但是,在极少数情况下,您可能会导致代码增长,以至于无法完全驻留在CPU缓存中(在性能关键的紧循环期间),您可能会遇到性能下降的问题。但是,如果您在该级别进行编码,那么您可能应该直接使用汇编语言进行编码

问题3

<代码>内联修饰符是编译器的一个提示,它可能需要考虑编译给定函数的内联。它不必遵循您的方向,结果也可能取决于给定的编译器选项。您可以随时查看生成的程序集代码,以找出它所做的。


您的基准测试甚至可能没有达到您想要的效果,因为您的编译器可能足够聪明,可以看到您甚至没有使用分配给
i
的函数调用的结果,因此它甚至不需要调用您的函数。再次,请查看生成的汇编代码。

交换是一个关于交换不同函数的操作系统术语运行进程的内存进出页面。基本上,交换需要一些时间。应用程序越大,交换的次数就越多

当你内联一个函数时,不是跳转到一个子例程,而是在调用位置转储整个函数的副本。这会使你的程序更大,因此理论上会导致更多的交换


通常情况下,对于非常小的方法(如powA和powB),内联应该是可以的,并且会导致更快的执行,但它实际上只是“理论上”——可能存在“更大的问题”在压缩代码的最后一点性能方面。

书籍中的语句是正确的。换句话说,如果操作得当,
内联可以提高性能,如果操作不当则会降低性能

最好只内联小函数。这将减少在内存中跳转的额外程序集调用。这就是如何提高性能的方法


如果使用
inline
大型函数,这可能会导致内存分页超过缓存大小,从而导致额外的内存交换。这就是性能受到阻碍的原因。

这两个语句都是正确的。声明函数
inline
是编译器的一个指标,如果可以的话,编译器将(通常)使用自己判断是否实际内联,但在C++中声明它<代码>内联确实改变代码生成,至少对于符号生成。 在此上下文中,“交换”指将可执行映像分页到磁盘。由于可执行映像较大,因此可能会影响内存受限系统中的性能


在回答第三个问题时,编译器选择了相同的行为(我猜是非内联的)对于这两个函数。

当编译一个普通函数时,它的机器代码被编译一次,并放在与调用它的其他函数分开的一个位置。当执行代码时,处理器必须跳转到存储代码的位置,而这条
跳转
指令需要额外的时间从内存加载函数。Som有时,调用一个函数(例如虚拟函数)需要几次跳转(或几次加载和一次跳转)。还需要花费一些时间来保存和恢复寄存器,以及创建堆栈帧,对于足够小的内联函数来说,这些都不是真的需要的

编译内联函数时,它的所有机器代码都直接插入到调用它的位置,因此
跳转
指令的时间被消除。编译器还根据内联函数的环境优化其代码(例如,登记赋值既可以考虑函数外的变量,也可以考虑函数内的变量以最小化需要保存的寄存器的数目)。然而,内联函数的代码可能出现在调用函数中的多个位置(如果调用代码中多次调用它),因此总的来说,它会使您的代码库变大。这会导致您的代码变得足够大,不再适合CPU缓存,在这种情况下,处理器必须转到主内存来获取您的代码,这比从缓存获取所有内容所需的时间要长。在某些情况下,这可以抵消消除
跳转带来的节省
指令,并使代码比内联代码时慢

“交换”通常指的是虚拟内存的行为,它与CPU缓存有着相同的权衡,但从磁盘加载代码所需的时间要长得多,并且程序必须填充的内存量要大得多