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

Is it worth inlining a function containing one tight loop?

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

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 的原因吗?

我们不应该内联这样的函数还有其他原因吗?

However, if we inline the function and the code gets inserted as a part of some bigger block, then i and n might end up on the stack and the for loop would be accessing RAM to continuously query their values.

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

例如:

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

关于inline关键字:这纯粹是一个提示。现代编译器不会尊重它。如果你想 force 内联,那么你可以使用 compiler-specific 标志,例如 GCC 或 Clang:

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

是否值得内联这样的函数?事先不能说。人们只能猜测,但即便如此,猜测也需要查看实际函数调用周围的代码。一般来说,你只能在测试和分析内联版本的代码与 non-inlined 版本的代码后,判断内联是否有用。

Are there other reasons why we should not inline such function?

正如@P__J__在评论中指出的那样:如果在很多地方调用内联函数,内联会增加程序的内存占用。因此,许多编译器对所有 and/or 这个特定内联函数的大小都有限制。通常内联会增加程序代码的大小。

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

Is this a reason to not use the keyword inline for such function?

直接回答:没有。 编译器会注意到 in 被频繁访问,并为它们分配寄存器,至少在循环期间是暂时的。即使周围的功能使用所有可用的寄存器。

取决于你所说的“价值”是什么意思。

如果您打算非常频繁地调用这些函数并且函数调用开销(为调用做准备、调用本身、函数序言和结尾)很重要,那么它是值得的。但是 inline 关键字对于编译器来说比顺序更像是一个提示。

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

https://godbolt.org/z/oKMeej

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

https://godbolt.org/z/jTaMc4

或者如果您不想内联函数(例如在调试期间)

https://godbolt.org/z/M4j5E7

inline是用词不当,基本上是修饰linkage的关键字。 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 不是 内联调用 - 它只是一个 提示 编译器 可能 考虑内联函数,但是......编译器可能会考虑任何内联 - 暗示他没有意义。如今的编译器积极优化,可以使用 link 时间优化,可以跨多个翻译单元合并相同的函数定义——完全忘记 inline。现在我看到使用 inline onlystatic 函数来禁用未使用的函数警告。 static inline 函数是 gnu89 和现在 C 之间唯一可移植的。

Is this a reason to not use the keyword inline for such function?

没有

So would it be worth to force inlining such function, or is there another reason for not doing that?

记得一下rules of optimization。不要通过猜测进行优化。只有检查生成的程序集或分析在特定编译器、编译器版本和编译器选项下编译的代码才能回答此类代码是否强制内联的问题。