Why should I ever use inline code?

2019-01-08 05:28发布

I'm a C/C++ developer, and here are a couple of questions that always baffled me.

  • 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

16条回答
做自己的国王
2楼-- · 2019-01-08 05:39

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.

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.

查看更多
我命由我不由天
3楼-- · 2019-01-08 05:39

If you are marking your code as inline in f.e. C++ you are also telling your compiler that the code should be executed inline, ie. that code block will "more or less" be inserted where it is called (thus removing the pushing, popping and jumping on the stack). So, yes... it is recommended if the functions are suitable for that kind of behavior.

查看更多
地球回转人心会变
4楼-- · 2019-01-08 05:39

Inlining usually is enabled at level 3 of optimization (-O3 in case of GCC). It can be a significant speed improvement in some cases (when it is possible).

Explicit inlining in your programs can add some speed improvement with the cost of an incresed code size.

You should see which is suitable: code size or speed and decide wether you should include it in your programs.

You can just turn on level 3 of optimization and forget about it, letting the compiler do his job.

查看更多
我想做一个坏孩纸
5楼-- · 2019-01-08 05:40

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.

However, modern compilers are very good at inlining function calls automatically without any prompt from the user when set to high optimisation. Actually, compilers are usually better at 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!

Additionally, compilers can and will ignore the 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.

One Definition Rule

However, declaring an inline function using the inline keyword has other effects, and may actually be necessary to 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.

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.

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.

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.

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 name of our function mean).

If, however, you uncomment the inline modifier in front of the function definition, the code compiles and links correctly.

Function templates are a special case: they are always inline, regardless of whether they were declared that way. This doesn’t mean that the compiler will inline calls to them, but they won’t violate ODR. The same is true for member functions that are defined inside a class or struct.

查看更多
唯我独甜
6楼-- · 2019-01-08 05:41

By inlining, the compiler inserts the implementation of the function, at the calling point. What you are doing with this is removing the function call overhead. However, there is no guarantee that your all candidates for inlining will actually be inlined by the compiler. However, for smaller functions, compilers always inline. So if you have a function that is called many times but only has a limited amount of code - a couple of lines - you could benefit from inlining, because the function call overhead might take longer than the execution of the function itself.

A classic example of a good candidate for inlining are getters for simple concrete classes.

CPoint
{
  public:

    inline int x() const { return m_x ; }
    inline int y() const { return m_y ; }

  private:
    int m_x ;
    int m_y ;

};

Some compilers ( e.g. VC2005 ) have an option for aggressive inlining, and you wouldn't need to specify the 'inline' keyword when using that option.

查看更多
乱世女痞
7楼-- · 2019-01-08 05:42

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.

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).

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.

查看更多
登录 后发表回答