C ++ proverbs: Understanding the inline intervention and Elimination

Source: Internet
Author: User
C ++ proverbs: Understand the inline-based intervention and exclusion-Linux general technology-Linux programming and kernel information. The following is a detailed description. Inline functions-Great idea! They look like functions, and they produce effects like functions. They are much better than Macros in various aspects, but you can call them without incurring the cost of function calls. What more requirements do you have?

In fact, you may get more than you think, because the cost of avoiding function calls is only part of the story. In typical cases, compiler optimization is designed for a continuous code without function calls. Therefore, when you inline a function, you may enable the compiler to perform context-related special Optimization on the function body. Most compilers do not implement such optimization for "outlined" function calls.

However, in programming, there is no free lunch in life, and the inline function is no exception. The idea behind an inline function is to use the function ontology to replace every call to this function without holding the Ph in the statistical table. d. as you can see, this may increase the size of your target code. On machines with limited memory, over-enthusiasm for inline will make the program too large for available space. Even if virtual memory is used, the Code expansion caused by inline will also lead to additional paging scheduling, reducing the Instruction Cache hit rate and the resulting performance loss.

On the other hand, if an inline function ontology is short, the code generated for the function ontology may be smaller than the code generated for a function call. In this case, the inline function can actually lead to smaller target code and higher Instruction Cache hit rate! Remember, inline is a request sent to the compiler, not a command. This request can be raised explicitly or implicitly. The implicit method is to define a function within the class definition:

Class Person {
Public:
...
Int age () const {return theAge;} // an implicit inline request: age is
... // Defined in a class definition

Private:
Int theAge;
};

Such functions are usually member functions, but we know that they can also be defined inside the class. If they are there, they are implicitly declared as inline.

The explicit way to declare an inline function is to add the inline keyword before its declaration. For example, the following are commonly used implementation methods of the standard max template (from:

Template // an explicit inline
Inline const T & std: max (const T & a, const T & B) // request: std: max is
{Return a <B? B: a;} // preceded by "inline"

The fact that max is a template leads to an observation conclusion: inline functions and templates are generally defined in the header file. This allows some programmers to conclude that the function template must be inline. This conclusion is illegal and potentially harmful, so it is worth looking. The inline function must be included in the header file, because most build environments are inline during compilation. To replace a function call with the called function ontology, the compiler must know what the function looks like. (Some build environments can be inline-based during connection, and a few others-for example, the control environment Based on. NET Common Language Infrastructure (CLI)-can be inline-based at runtime. However, these environments are all exceptions and are not rules. Inline is a compile-time behavior in most C ++ programs .)

The template is generally in the header file, because the compiler needs to know what a template looks like so that it can be instantiated when used. (Not all. Some build environments can be instantiated during the connection. However, instantiation during compilation is more common .) Template instantiation has nothing to do with inline. If you write a template and you think that all functions instantiated from this template should be inline, then declare this template as inline, which is the above std :: the implementation of max is done. However, if you write a template for a function that has no reason to be inline-based, you should avoid declaring this template as inline (whether explicit or implicit ). Inline is costly, and you don't want to encounter them without foresight. We have already talked about how inline will cause code expansion, but there are other costs. We will discuss it later.

Before doing this, let's first examine this conclusion: inline is a request that may be ignored by the compiler. Most compilers reject inline functions that they think are too complex (for example, those that contain loops or recursion), and calls to all the virtual functions except the most detailed ones will not be made inline. We should not be surprised by this conclusion. Virtual means "waiting until the runtime can determine which function is called", and inline means "replacing the called place with the called function before execution ". If the compiler does not know which function will be called, it is hard to blame them for rejecting the inline function ontology.

Together, we can conclude that whether a specified inline function can be truly converted into inline depends on the Build Environment you are using-mainly the compiler. Fortunately, most compilers have a diagnostic level that generates a warning when they cannot inline a function you propose.

Sometimes, even if the compiler is willing to inline a function, they will still generate a function Ontology for this inline function. For example, if your program needs to hold the address of an inline function, the compiler must generate an outlined function Ontology for it. How can they generate a pointer to a function that does not exist at all? In addition, the compiler generally does not inline calls through function pointers, which means that calls to an inline function may or may not be inline, depends on how this call is made:

Inline void f () {...} // assume compilers are willing to inline callto f

Void (* pf) () = f; // pf points to f
...

F (); // this call will be inlined, because it's a "normal" call
Pf (); // this call probably won't be, because it's through
// A function pointer

Even if you have never used function pointers, the shadows of inline functions will visit you from time to time, because programmers are not necessarily the only ones who need function pointers. Sometimes the compiler generates out-of-line copies of constructor and destructor so that they can obtain pointers to these functions, used to construct and analyze objects in the array.

In fact, constructor and destructor are often a much worse candidate for inlineization than you can display in casual checks. For example, consider the following class Derived constructor:

Class Base {
Public:
...

Private:
Std: string bm1, bm2; // base members 1 and 2
};

Class Derived: public Base {
Public:
Derived () {}// Derived's ctor is empty-or is it?
...

Private:
Std: string dm1, dm2, dm3; // derived members 1-3
};

This constructor looks like an excellent inline candidate because it does not contain code. However, vision will be spoofed.

C ++ provides various guarantees for what happens when an object is created and destroyed. For example, when you use new, your dynamically created objects will be automatically initialized by their constructors, And when you use delete. The corresponding destructor will be called. When you create an object, each base class and each data member of the object are automatically constructed. when an object is destroyed, a reverse process about the structure is triggered. If an exception is thrown during the construction of an object, any part of the constructed object will be automatically destroyed. In all these cases, C ++ only says what must happen, but does not say how to happen. This is the real thing of the compiler, but obviously these things won't happen on their own. There must be some code in your program to make this happen, and the code -- the code written by the compiler and the code inserted into your program during compilation -- must be somewhere. Sometimes they end up in constructor and destructor, so we can imagine that the code generated for the above-mentioned Derived constructor is equivalent to the following:

Derived: Derived () // conceptual implementation
{
// "Empty" Derived ctor

Base: Base (); // initialize Base part

Try {dm1.std: string ();} // try to construct dm1
Catch (...) {// if it throws,
Base ::~ Base (); // destroy base class part and
Throw; // propagate the exception
}

Try {dm2.std: string ();} // try to construct dm2
Catch (...){
// If it throws,
Dm1.std: string ::~ String (); // destroy dm1,
Base ::~ Base (); // destroy base class part, and
Throw; // propagate the exception
}

Try {dm3.std: string ();} // construct dm3
Catch (...){
// If it throws,
Dm2.std: string ::~ String (); // destroy dm2,
Dm1.std: string ::~ String (); // destroy dm1,
Base ::~ Base (); // destroy base class part, and
Throw; // propagate the exception
}
}

These codes do not represent the real compiler generation, because the real compiler will handle exceptions in a more complex way. Nevertheless, it accurately reflects the actions that the Derived "null" constructor must provide. No matter how complicated a compiler is, the Derived constructor must at least call its data members and constructors of the base class. These calls (they may also be inline) will affect its attractiveness to inline.

The same reason applies to the Base constructor, so if it is inline, all the code to insert it also inserts the Derived Constructor (call the Base constructor through the Derived constructor ). And if the string constructor happens to be inline, the Derived constructor will add five copies of the function code, it corresponds to five strings in the Derived object respectively (Two Inherited and three declared by itself ). Maybe at present, it is not clear whether the constructor of line-based Derived is a decision without the brain. Similar considerations apply to the Derived destructor. using the same or different methods, you must ensure that all objects initialized by the Derived constructor are completely destroyed.

The library designer must evaluate the impact of declaring a function as inline, because it is impossible to provide a binary upgrade for the inline function visible to customers in the library. In other words, if f is an inline function in a library, the Library's customers will compile the function f ontology into their applications. If the implementer of a database decides to modify f later, all the customers who use f must re-compile. This is often annoying. On the other hand, if f is a non-inline function, the change to f only requires the customer to reconnect. This is obviously easier than re-compilation. If the functions in the library are dynamically linked, this is a completely transparent method for users.
 
For the purpose of program development, it is important to keep in mind these things that need to be considered. But from the practical point of view during coding, the dominant fact is: most debuggers may conflict with the inline function. This should not be a major discovery. How can you set a breakpoint in a function that is not there? Although some build environments try to support inline function debugging, most environments simply cancel the inline for the debug build.

This exports a logic policy to determine which functions should be declared as inline and which should not. At first, don't use inline anything, or at least limit your inline scope to those functions that must be inline and that are really trivial. By using inline with caution, you can make it easy to use the debugger, but you also put inline into the position where it should have been: as a manual optimization. Do not forget the 80-20 rule based on experience. It claims that a typical program uses 80% of the time to execute 20% of the Code. This is an important rule because it reminds you that as a software developer, the goal is to identify code that can comprehensively improve your program performance by 20%. You can adjust your function indefinitely in inline or other ways, but unless you concentrate on the Right function, it is a waste of energy.

Things to Remember

· Limit most of inlines to small and frequently called functions. This makes program debugging and binary upgrades easier, minimizes potential code expansion, and maximizes the chance of program speed.

· Do not declare the function template as inline just because it appears in the header file.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.