C++ 为什么我应该使用内联代码?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/132738/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why should I ever use inline code?
提问by Marcos Bento
I'm a C/C++ developer, and here are a couple of questions that always baffled me.
我是一名 C/C++ 开发人员,这里有几个问题一直困扰着我。
- Is there a big difference between "regular" code and inline code?
- Which is the main difference?
- Is inline code simply a "form" of macros?
- What kind of tradeoff must be done when choosing to inline your code?
- “常规”代码和内联代码之间有很大区别吗?
- 主要区别是什么?
- 内联代码只是宏的“形式”吗?
- 选择内联代码时必须进行什么样的权衡?
Thanks
谢谢
回答by Konrad Rudolph
Performance
表现
As has been suggested in previous answers, use of the inline
keyword can make code faster by inlining function calls, often at the expense of increased executables. “Inlining function calls” just means substituting the call to the target function with the actual code of the function, after filling in the arguments accordingly.
正如之前的答案中所建议的那样,使用inline
关键字可以通过内联函数调用使代码更快,通常以增加可执行文件为代价。“内联函数调用”只是意味着在相应地填写参数后,用函数的实际代码替换对目标函数的调用。
However, modern compilers are very good at inlining function calls automatically without any prompt from the userwhen set to high optimisation. Actually, compilers are usually betterat determining what calls to inline for speed gain than humans are.
然而,现代编译器非常擅长在设置为高度优化时自动内联函数调用而无需用户提示。实际上,编译器通常比人类更擅长确定什么调用内联以获得速度增益。
Declaring functions inline
explicitly for the sake of performance gain is (almost?) always unnecessary!
inline
为了性能提升而显式声明函数是(几乎?)总是没有必要的!
Additionally, compilers can and willignorethe inline
request if it suits them. Compilers will do this if a call to the function is impossible to inline (i.e. using nontrivial recursion or function pointers) but also if the function is simply too large for a meaningful performance gain.
此外,编译器可以并且将忽略inline
适合他们的请求。如果对函数的调用不可能内联(即使用非平凡的递归或函数指针),而且如果函数太大而无法获得有意义的性能增益,编译器就会这样做。
One Definition Rule
一个定义规则
However, declaring an inline function using the inline
keyword has other effects, and may actually be necessaryto satisfy the One Definition Rule (ODR): This rule in the C++ standard states that a given symbol may be declared multiple times but may only be defined once. If the link editor (= linker) encounters several identical symbol definitions, it will generate an error.
但是,使用inline
关键字声明内联函数还有其他效果,实际上可能需要满足单一定义规则 (ODR):C++ 标准中的这条规则规定,给定的符号可以被多次声明,但只能被定义一次. 如果链接编辑器(=链接器)遇到几个相同的符号定义,就会产生错误。
One solution to this problem is to make sure that a compilation unit doesn't export a given symbol by giving it internal linkage by declaring it static
.
这个问题的一个解决方案是确保编译单元不会通过声明它来为其提供内部链接来导出给定符号static
。
However, it's often better to mark a function inline
instead. This tells the linker to merge all definitions of this function across compilation units into one definition, with one address, and shared function-static variables.
但是,通常最好标记一个函数inline
。这告诉链接器将跨编译单元的该函数的所有定义合并为一个定义,具有一个地址和共享函数静态变量。
As an example, consider the following program:
例如,考虑以下程序:
// header.hpp
#ifndef HEADER_HPP
#define HEADER_HPP
#include <cmath>
#include <numeric>
#include <vector>
using vec = std::vector<double>;
/*inline*/ double mean(vec const& sample) {
return std::accumulate(begin(sample), end(sample), 0.0) / sample.size();
}
#endif // !defined(HEADER_HPP)
// test.cpp
#include "header.hpp"
#include <iostream>
#include <iomanip>
void print_mean(vec const& sample) {
std::cout << "Sample with x? = " << mean(sample) << '\n';
}
// main.cpp
#include "header.hpp"
void print_mean(vec const&); // Forward declaration.
int main() {
vec x{4, 3, 5, 4, 5, 5, 6, 3, 8, 6, 8, 3, 1, 7};
print_mean(x);
}
Note that both .cpp
files include the header file and thus the function definition of mean
. Although the file is saved with include guards against double inclusion, this will result in two definitions of the same function, albeit in different compilation units.
请注意,这两个.cpp
文件都包含头文件,因此包含mean
. 尽管文件保存时使用了包含防止双重包含的保护措施,但这将导致同一函数的两个定义,尽管在不同的编译单元中。
Now, if you try to link those two compilation units —?for example using the following command:
现在,如果您尝试链接这两个编译单元——例如使用以下命令:
??? g++ -std=c++11 -pedantic main.cpp test.cpp
you'll get an error saying “duplicate symbol __Z4meanRKNSt3__16vectorIdNS_9allocatorIdEEEE” (which is the mangled nameof our function mean
).
你会得到一个错误,说“重复符号 __Z4meanRKNSt3__16vectorIdNS_9allocatorIdEEEE”(这是我们函数的错误名称mean
)。
If, however, you uncomment the inline
modifier in front of the function definition, the code compiles and links correctly.
但是,如果您取消对inline
函数定义前面的修饰符的注释,则代码将正确编译和链接。
Function templatesare a special case: they are alwaysinline, regardless of whether they were declared that way. This doesn't mean that the compiler will inline callsto them, but they won't violate ODR. The same is true for member functions that are defined inside a class or struct.
函数模板是一个特例:它们总是内联的,无论它们是否以这种方式声明。这并不意味着编译器会内联调用它们,但它们不会违反 ODR。对于在类或结构中定义的成员函数也是如此。
回答by Luc Touraille
- Is there a big difference between "regular" code and inline code?
- “常规”代码和内联代码之间有很大区别吗?
Yes and no. No, because an inline function or method has exactly the same characteristics as a regular one, most important one being that they are both type safe. And yes, because the assembly code generated by the compiler will be different; with a regular function, each call will be translated into several steps: pushing parameters on the stack, making the jump to the function, popping the parameters, etc, whereas a call to an inline function will be replaced by its actual code, like a macro.
是和否。不,因为内联函数或方法与常规函数或方法具有完全相同的特征,最重要的是它们都是类型安全的。是的,因为编译器生成的汇编代码会有所不同;对于常规函数,每次调用都将被转换为几个步骤:将参数压入堆栈、跳转到函数、弹出参数等,而对内联函数的调用将被其实际代码替换,例如宏。
- Is inline code simply a "form" of macros?
- 内联代码只是宏的“形式”吗?
No! A macro is simple text replacement, which can lead to severe errors. Consider the following code:
不!宏是简单的文本替换,可能会导致严重错误。考虑以下代码:
#define unsafe(i) ( (i) >= 0 ? (i) : -(i) )
[...]
unsafe(x++); // x is incremented twice!
unsafe(f()); // f() is called twice!
[...]
Using an inline function, you're sure that parameters will be evaluated before the function is actually performed. They will also be type checked, and eventually converted to match the formal parameters types.
使用内联函数,您可以确保在实际执行函数之前评估参数。它们还将进行类型检查,并最终转换为匹配形式参数类型。
- What kind of tradeoff must be done when choosing to inline your code?
- 选择内联代码时必须进行什么样的权衡?
Normally, program execution should be faster when using inline functions, but with a bigger binary code. For more information, you should read GoTW#33.
通常,使用内联函数时程序执行速度应该更快,但使用更大的二进制代码。有关更多信息,您应该阅读GoTW#33。
回答by Sander
Inline code works like macros in essence but it is actual real code, which can be optimized. Very small functions are often good for inlining because the work needed to set up the function call (load the parameters into the proper registers) is costly compared to the small amount of actual work the method does. With inlining, there is no need to set up the function call, because the code is directly "pasted into" any method that uses it.
内联代码本质上像宏一样工作,但它是实际的真实代码,可以优化。非常小的函数通常适用于内联,因为与该方法所做的少量实际工作相比,设置函数调用(将参数加载到正确的寄存器)所需的工作成本很高。使用内联,无需设置函数调用,因为代码直接“粘贴到”任何使用它的方法中。
Inlining increases code size, which is its primary drawback. If the code is so big that it cannot fit into the CPU cache, you can get major slowdowns. You only need to worry about this in rare cases, since it is not likely you are using a method in so many places the increased code would cause issues.
内联增加了代码大小,这是它的主要缺点。如果代码太大以至于无法放入 CPU 缓存,则可能会出现严重的减速。您只需要在极少数情况下担心这一点,因为您不太可能在如此多的地方使用一种方法,增加的代码会导致问题。
In summary, inlining is ideal for speeding up small methods that are called many times but not in too many places (100 places is still fine, though - you need to go into quite extreme examples to get any significant code bloat).
总而言之,内联非常适合加速被多次调用但不会在太多地方调用的小方法(不过,100 个地方仍然可以——你需要进入非常极端的例子来获得任何显着的代码膨胀)。
Edit: as others have pointed out, inlining is only a suggestion to the compiler. It can freely ignore you if it thinks you are making stupid requests like inlining a huge 25-line method.
编辑:正如其他人所指出的,内联只是对编译器的建议。如果它认为你在提出愚蠢的请求,比如内联一个巨大的 25 行方法,它可以无视你。
回答by Adam Davis
- Is there a big difference between "regular" code and inline code?
- “常规”代码和内联代码之间有很大区别吗?
Yes - inline code does not involve a function call, and saving register variables to the stack. It uses program space each time it is 'called'. So overall it takes less time to execute because there's no branching in the processor and saving of state, clearing of caches, etc.
是 - 内联代码不涉及函数调用,也不涉及将寄存器变量保存到堆栈中。每次“调用”时它都会使用程序空间。因此,总体而言,执行所需的时间更少,因为处理器中没有分支,也没有保存状态、清除缓存等。
- Is inline code simply a "form" of macros?
- 内联代码只是宏的“形式”吗?
Macros and inline code share similarities. the big difference is that the inline code is specifically formatted as a function so the compiler, and future maintainers, have more options. Specifically it can easily be turned into a function if you tell the compiler to optimize for code space, or a future maintainer ends up expanding it and using it in many places in their code.
宏和内联代码有相似之处。最大的区别在于内联代码被专门格式化为一个函数,因此编译器和未来的维护者有更多的选择。具体来说,如果你告诉编译器优化代码空间,或者未来的维护者最终扩展它并在他们代码的许多地方使用它,它可以很容易地变成一个函数。
What kind of tradeoff must be done when choosing to inline your code?
- Macro: high code space usage, fast execution, hard to maintain if the 'function' is long
- Function: low code space usage, slower to execute, easy to maintain
- Inline function: high code space usage, fast execution, easy to maintain
选择内联代码时必须进行什么样的权衡?
- 宏:代码空间占用高,执行速度快,如果“函数”很长,则难以维护
- 功能:代码空间占用低,执行速度较慢,易于维护
- 内联函数:代码空间占用高,执行速度快,易于维护
It should be noted that the register saving and jumping to the function does take up code space, so for very small functions an inline cantake up less space than a function.
应该注意的是,寄存器保存和跳转到函数确实会占用代码空间,因此对于非常小的函数,内联可以比函数占用更少的空间。
-Adam
-亚当
回答by Kasprzol
Inline differs from macros in that it's a hint to the compiler (compiler may decide not to inline the code!) and macros are source code text generation before the compilation and as such are "forced" to be inlined.
内联与宏的不同之处在于,它是对编译器的一个提示(编译器可能决定不内联代码!),而宏是在编译之前生成的源代码文本,因此被“强制”内联。
回答by PhiLho
It depends on the compiler...
Say you have a dumb compiler. By indicating a function must be inlined, it will put a copy of the content of the function on each occurrence were it is called.
这取决于编译器......
假设你有一个愚蠢的编译器。通过指示必须内联函数,它将在每次调用时放置该函数内容的副本。
Advantage: no function call overhead (putting parameters, pushing the current PC, jumping to the function, etc.). Can be important in the central part of a big loop, for example.
优点:没有函数调用开销(放入参数、推送当前PC、跳转到函数等)。例如,在大循环的中心部分可能很重要。
Inconvenience: inflates the generated binary.
不便:膨胀生成的二进制文件。
Is it a macro? Not really, because the compiler still checks the type of parameters, etc.
是宏吗?不是真的,因为编译器仍然会检查参数的类型等。
What about smart compilers? They can ignore the inline directive, if they "feel" the function is too complex/too big. And perhaps they can automatically inline some trivial functions, like simple getters/setters.
智能编译器呢?如果他们“觉得”函数太复杂/太大,他们可以忽略内联指令。也许他们可以自动内联一些琐碎的函数,比如简单的 getter/setter。
回答by Charles Duffy
Marking a function inline means that the compiler has the optionto include in "in-line" where it is called, if the compiler chooses to do so; by contrast, a macro will alwaysbe expanded in-place. An inlined function will have appropriate debug symbols set up to allow a symbolic debugger to track the source where it came from, while debugging macros is confusing. Inline functions need to be valid functions, while macros... well, don't.
将函数标记为内联意味着编译器可以选择将其包含在“内联”中,如果编译器选择这样做的话;相比之下,宏将始终就地展开。内联函数将设置适当的调试符号,以允许符号调试器跟踪它的来源,而调试宏则令人困惑。内联函数需要是有效的函数,而宏......好吧,不要。
Deciding to declare a function inline is largely a space tradeoff -- your program will be larger if the compiler decides to inline it (particularly if it isn't also static, in which case at least one non-inlined copy is required for use by any external objects); indeed, if the function is large, this could result in a drop in performance as less of your code fits in cache. The general performance boost, however, is just that you're getting rid of the overhead of the function call itself; for a small function called as part of an inner loop, that's a tradeoff that makes sense.
决定内联声明一个函数在很大程度上是一种空间权衡——如果编译器决定内联它,你的程序会更大(特别是如果它不是静态的,在这种情况下,至少需要一个非内联副本供使用)任何外部对象);事实上,如果函数很大,这可能会导致性能下降,因为缓存中适合的代码较少。然而,一般的性能提升只是消除了函数调用本身的开销;对于作为内部循环的一部分调用的小函数,这是一个有意义的权衡。
If you trust your compiler, mark small functions used in inner loops inline
liberally; the compiler will be responsible for Doing The Right Thing in deciding whether or not to inline.
如果您信任您的编译器,请随意标记内循环中使用的小函数inline
;编译器将负责做正确的事情来决定是否内联。
回答by stu
The answer of should you inline comes down to speed. If you're in a tight loop calling a function, and it's not a super huge function, but one where a lot of the time is wasted in CALLING the function, then make that function inline and you'll get a lot of bang for your buck.
你应该内联的答案归结为速度。如果你在一个紧密的循环中调用一个函数,并且它不是一个超级庞大的函数,而是一个在调用函数上浪费了大量时间的函数,那么将该函数内联,你会得到很多你的钱。
回答by yesraaj
First of all inline is a request to compiler to inline the function .so it is upto compiler to make it inline or not.
首先 inline 是要求编译器内联函数。因此编译器是否使其内联。
- When to use?When ever a function is of very few lines(for all accessors and mutator) but not for recursive functions
- Advantage?Time taken for invoking the function call is not involved
- Is compiler inline any function of its own?yes when ever a function is defined in header file inside a class
- 什么时候使用? 当一个函数只有很少的行(对于所有访问器和修改器)而不是递归函数时
- 优点?不涉及调用函数调用的时间
- 编译器是否内联了它自己的任何函数?是的,当在类的头文件中定义了一个函数时
回答by yesraaj
inlining is a technique to increase speed. But use a profiler to test this in your situation. I have found (MSVC) that inlining does not always deliver and certainly not in any spectacular way. Runtimes sometimes decreased by a few percent but in slightly different circumstances increased by a few percent.
内联是一种提高速度的技术。但是在你的情况下使用分析器来测试这个。我发现 (MSVC) 内联并不总是能够交付,当然也不会以任何壮观的方式交付。运行时间有时会减少几个百分点,但在略有不同的情况下会增加几个百分点。
If the code is running slowly, get out your profiler to find troublespots and work on those.
如果代码运行缓慢,请使用分析器查找问题点并解决这些问题。
I have stopped adding inline functions to header files, it increases coupling but gives little in return.
我已经停止向头文件添加内联函数,它增加了耦合但几乎没有回报。