[9] Inline functions
(Part of C++ FAQ Lite, Copyright © 1991-2006, Marshall Cline, cline@parashift.com)


FAQs in section [9]:


[9.1] What's the deal with inline functions?

When the compiler inline-expands a function call, the function's code gets inserted into the caller's code stream (conceptually similar to what happens with a #define macro). This can, depending on a zillion other things, improve performance, because the optimizer can procedurally integrate the called code — optimize the called code into the caller.

There are several ways to designate that a function is inline, some of which involve the inline keyword, others do not. No matter how you designate a function as inline, it is a request that the compiler is allowed to ignore: it might inline-expand some, all, or none of the calls to an inline function. (Don't get discouraged if that seems hopelessly vague. The flexibility of the above is actually a huge advantage: it lets the compiler treat large functions differently from small ones, plus it lets the compiler generate code that is easy to debug if you select the right compiler options.)

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[9.2] What's a simple example of procedural integration?

Consider the following call to function g():

 void f()
 {
   int x = 
/*...*/;
   int y = 
/*...*/;
   int z = 
/*...*/;
   
...code that uses xy and z...
   g(x, y, z);
   
...more code that uses xy and z...
 }

Assuming a typical C++ implementation that has registers and a stack, the registers and parameters get written to the stack just before the call to g(), then the parameters get read from the stack inside g() and read again to restore the registers while g() returns to f(). But that's a lot of unnecessary reading and writing, especially in cases when the compiler is able to use registers for variables x, y and z: each variable could get written twice (as a register and also as a parameter) and read twice (when used within g() and to restore the registers during the return to f()).

 void g(int x, int y, int z)
 {
   
...code that uses xy and z...
 }

If the compiler inline-expands the call to g(), all those memory operations could vanish. The registers wouldn't need to get written or read since there wouldn't be a function call, and the parameters wouldn't need to get written or read since the optimizer would know they're already in registers.

Naturally your mileage may vary, and there are a zillion variables that are outside the scope of this particular FAQ, but the above serves as an example of the sorts of things that can happen with procedural integration.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[9.3] Do inline functions improve performance?

Yes and no. Sometimes. Maybe.

There are no simple answers. inline functions might make the code faster, they might make it slower. They might make the executable larger, they might make it smaller. They might cause thrashing, they might prevent thrashing. And they might be, and often are, totally irrelevant to speed.

inline functions might make it faster: As shown above, procedural integration might remove a bunch of unnecessary instructions, which might make things run faster.

inline functions might make it slower: Too much inlining might cause code bloat, which might cause "thrashing" on demand-paged virtual-memory systems. In other words, if the executable size is too big, the system might spend most of its time going out to disk to fetch the next chunk of code.

inline functions might make it larger: This is the notion of code bloat, as described above. For example, if a system has 100 inline functions each of which expands to 100 bytes of executable code and is called in 100 places, that's an increase of 1MB. Is that 1MB going to cause problems? Who knows, but it is possible that that last 1MB could cause the system to "thrash," and that could slow things down.

inline functions might make it smaller: The compiler often generates more code to push/pop registers/parameters than it would by inline-expanding the function's body. This happens with very small functions, and it also happens with large functions when the optimizer is able to remove a lot of redundant code through procedural integration — that is, when the optimizer is able to make the large function small.

inline functions might cause thrashing: Inlining might increase the size of the binary executable, and that might cause thrashing.

inline functions might prevent thrashing: The working set size (number of pages that need to be in memory at once) might go down even if the executable size goes up. When f() calls g(), the code is often on two distinct pages; when the compiler procedurally integrates the code of g() into f(), the code is often on the same page.

inline functions might increase the number of cache misses: Inlining might cause an inner loop to span across multiple lines of the memory cache, and that might cause thrashing of the memory-cache.

inline functions might decrease the number of cache misses: Inlining usually improves locality of reference within the binary code, which might decrease the number of cache lines needed to store the code of an inner loop. This ultimately could cause a CPU-bound application to run faster.

inline functions might be irrelevant to speed: Most systems are not CPU-bound. Most systems are I/O-bound, database-bound or network-bound, meaning the bottleneck in the system's overall performance is the file system, the database or the network. Unless your "CPU meter" is pegged at 100%, inline functions probably won't make your system faster. (Even in CPU-bound systems, inline will help only when used within the bottleneck itself, and the bottleneck is typically in only a small percentage of the code.)

There are no simple answers: You have to play with it to see what is best. Do not settle for simplistic answers like, "Never use inline functions" or "Always use inline functions" or "Use inline functions if and only if the function is less than N lines of code." These one-size-fits-all rules may be easy to write down, but they will produce sub-optimal results.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[9.4] How can inline functions help with the tradeoff of safety vs. speed?

In straight C, you can achieve "encapsulated structs" by putting a void* in a struct, in which case the void* points to the real data that is unknown to users of the struct. Therefore users of the struct don't know how to interpret the stuff pointed to by the void*, but the access functions cast the void* to the appropriate hidden type. This gives a form of encapsulation.

Unfortunately it forfeits type safety, and also imposes a function call to access even trivial fields of the struct (if you allowed direct access to the struct's fields, anyone and everyone would be able to get direct access since they would of necessity know how to interpret the stuff pointed to by the void*; this would make it difficult to change the underlying data structure).

Function call overhead is small, but can add up. C++ classes allow function calls to be expanded inline. This lets you have the safety of encapsulation along with the speed of direct access. Furthermore the parameter types of these inline functions are checked by the compiler, an improvement over C's #define macros.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[9.5] Why should I use inline functions instead of plain old #define macros?

Because #define macros are evil in 4 different ways: evil#1, evil#2, evil#3, and evil#4. Sometimes you should use them anyway, but they're still evil.

Unlike #define macros, inline functions avoid infamous macro errors since inline functions always evaluate every argument exactly once. In other words, invoking an inline function is semantically just like invoking a regular function, only faster:

 // A macro that returns the absolute value of i
 #define unsafe(i)  \
         ( (i) >= 0 ? (i) : -(i) )
 
 
// An inline function that returns the absolute value of i
 inline
 int safe(int i)
 {
   return i >= 0 ? i : -i;
 }
 
 int f();
 
 void userCode(int x)
 {
   int ans;
 
   ans = unsafe(x++);   
// Error! x is incremented twice
   ans = unsafe(f());   
// Danger! f() is called twice
 
   ans = safe(x++);     
// Correct! x is incremented once
   ans = safe(f());     
// Correct! f() is called once
 }

Also unlike macros, argument types are checked, and necessary conversions are performed correctly.

Macros are bad for your health; don't use them unless you have to.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[9.6] How do you tell the compiler to make a non-member function inline?

When you declare an inline function, it looks just like a normal function:

 void f(int i, char c);

But when you define an inline function, you prepend the function's definition with the keyword inline, and you put the definition into a header file:

 inline
 void f(int i, char c)
 {
   
...
 }

Note: It's imperative that the function's definition (the part between the {...}) be placed in a header file, unless the function is used only in a single .cpp file. In particular, if you put the inline function's definition into a .cpp file and you call it from some other .cpp file, you'll get an "unresolved external" error from the linker.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[9.7] How do you tell the compiler to make a member function inline?

When you declare an inline member function, it looks just like a normal member function:

 class Fred {
 public:
   void f(int i, char c);
 };

But when you define an inline member function, you prepend the member function's definition with the keyword inline, and you put the definition into a header file:

 inline
 void Fred::f(int i, char c)
 {
   
...
 }

It's usually imperative that the function's definition (the part between the {...}) be placed in a header file. If you put the inline function's definition into a .cpp file, and if it is called from some other .cpp file, you'll get an "unresolved external" error from the linker.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[9.8] Is there another way to tell the compiler to make a member function inline?

Yep: define the member function in the class body itself:

 class Fred {
 public:
   void f(int i, char c)
     {
       
...
     }
 };

Although this is easier on the person who writes the class, it's harder on all the readers since it mixes "what" a class does with "how" it does them. Because of this mixture, we normally prefer to define member functions outside the class body with the inline keyword. The insight that makes sense of this: in a reuse-oriented world, there will usually be many people who use your class, but there is only one person who builds it (yourself); therefore you should do things that favor the many rather than the few. This approach is further exploited in the next FAQ.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[9.9] With inline member functions that are defined outside the class, is it best to put the inline keyword next to the declaration within the class body, next to the definition outside the class body, or both?

Best practice: only in the definition outside the class body.

 class Foo {
 public:
   void method();  
 best practice: don't put the inline keyword here
   
...
 };
 
 inline void Foo::method()  
 best practice: put the inline keyword here
 { ... }

Here's the basic idea:

From a practical standpoint, this separation makes life easier and safer for your users. Say Chuck wants to simply "use" your class. Because you read this FAQ and used the above separation, Chuck can read your class's public: part and see everything he needs to see and nothing he doesn't need to see. His life is easier because he needs to look in only one spot, and his life is safer because his pure mind isn't polluted by implementation minutiae.

Back to inline-ness: the decision of whether a function is or is not inline is an implementation detail that does not change the observable semantics (the "meaning") of a call. Therefore the inline keyword should go next to the function's definition, not within the class's public: part.

NOTE: most people use the terms "declaration" and "definition" to differentiate the above two places. For example, they might say, "Should I put the inline keyword next to the declaration or the definition?" Unfortunately that usage is sloppy and somebody out there will eventually gig you for it. The people who gig you are probably insecure, pathetic wannabes who know they're not good enough to actually acomplish something with their lives, nonetheless you might as well learn the correct terminology to avoid getting gigged. Here it is: every definition is also a declaration. This means using the two as if they are mutually exclusive would be like asking which is heavier, steel or metal? Almost everybody will know what you mean if you use "definition" as if it is the opposite of "declaration," and only the worst of the techie weenies will gig you for it, but at least you now know how to use the terms correctly.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


E-Mail E-mail the author
C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised Mar 1, 2006