内联包含一个紧密循环的函数值得吗?

问题描述

假设我们有一个仅包含一个紧密循环的函数,例如:

int Prime(int n) {
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            return 0;
        }
    }
    return 1;
}

是否值得内联以下功能inline int Prime(int n)?请注意,出于这个问题的目的,假设如果我们内联函数,将进行代码替换 。所以现在的问题是,在这种情况下这是否是一件好事。

我会说不,因为如果cpu执行一条call指令并开始执行该函数,而其他所有东西都被压入堆栈,那么整数in可能适合进入寄存器,整个for循环将仅在寄存器上执行。

但是,如果我们内联函数并且代码被插入为更大块的一部分,则in可能会最终进入堆栈,并且for循环将访问RAM来不断查询其值。

这是为什么不将关键字inline用于此类功能的原因吗?

还有其他原因为什么我们不应该内联这样的功能

解决方法

但是,如果我们内联函数并且代码被插入为更大块的一部分,则i和n可能会最终出现在堆栈上,并且for循环将访问RAM来连续查询其值。

并非如此,当使用适当的优化标志运行时,没有明智的编译器会将循环变量放入RAM。充其量,即使参数通过堆栈传递,您也将在循环开始之前看到一个或多个移入寄存器(在循环之前和之后使用适当的push / pop指令以保留寄存器的原始值)

例如:

push regA   ; save original regA
mov regA,2 ; use regA as i
; ... loop ...
pop regA    ; restore regA

关于inline关键字:纯粹是一个提示。现代编译器不会尊重它。如果要强制内联,则可以使用特定于编译器的标志,例如对于GCC或Clang:

inline __attribute__((always_inline)) int Prime(int n) { ...

内联这样的功能是否值得?无法事先说明。人们只能猜测,但是即使如此,猜测仍需要查看实际函数调用周围的代码。通常,您只能说在使用内联版本对代码进行测试和性能分析后再使用非内联版本对代码进行分析后,内联是否有用。

还有其他原因为什么我们不应该内联这样的功能?

正如@P__J__在注释中所指出的:如果在很多地方调用了内联函数,则内联会增加程序的内存占用。因此,许多编译器都限制了全部和/或此特定内联函数的大小。通常,内联会增加程序代码的大小。

如果您的目标是拥有一个非常小的程序,那么内联并不是一个好主意。

,

这是为什么不将关键字inline用于此类功能的原因吗?

直接回答:否。 编译器会注意到in经常被访问,并至少在循环过程中暂时为其分配寄存器。即使周围的功能使用了所有可用的寄存器。

,

取决于您所说的“价值”。

如果您将非常频繁地调用这些函数,并且函数调用开销(准备调用,调用本身,函数序言和结尾)很重要,那么这是值得的。但是inline关键字对编译器而言不是一个提示,而不是顺序。

在许多情况下,即使没有inline关键字,编译器也会内联它。

https://godbolt.org/z/oKMeej

如果要确保始终将函数内联,则通常需要使用一些编译器扩展来强制内联。

https://godbolt.org/z/jTaMc4

或者如果您不想内联函数(例如在调试过程中)

https://godbolt.org/z/M4j5E7

,

inline是用词不当,基本上是修改链接的关键字。 inline存在,因此您可以提供编译器可以选择的两个函数:

// file.h
// a definition of the inline function
inline void function(void) {
    // some algorithm
}
// forward declaration of not inline function
void function(void);


// file.c
void function(void) {
   // the same algorithm
}

// main.h
#include "file.h"
int main() {
    function(); // will it call file.h:function? or file.c:function?
                // compiler may choose
}

inline不是内联调用-只是编译器可以考虑内联的函数的提示。 ..编译器可能会考虑任何内联的内容-毫无暗示可言。使用当今可以进行积极优化的编译器,可以使用链接时间优化,可以在多个翻译单元中合并相同的函数定义-根本就不用担心inline了。如今,我看到inline函数仅使用static 来禁用未使用的函数警告。 static inline函数是在gnu89和当今C之间唯一可移植的函数。

这是为什么不将关键字inline用于此类功能的原因吗?

否。

那么强制内联这样的功能是否值得,还是有其他理由不这样做呢?

请记住rules of optimization。不要通过猜测来优化。仅检查生成的程序集或在特定编译器,编译器版本和编译器选项下编译的代码的性能分析,才能回答是否强制插入此类代码的问题。