Inline Specifier Compilation in C/C++
Introduction
The intent of the inline
specifier is to serve as a hint for the compiler to perform optimizations, such as function inlining, which usually require the definition of a function to be visible at the call site. So with the inline
specifier, the compiler might create an optimized program which runs faster.
In this blog post, I would like to discuss the inline optimization mechanism and the best practice of creating inline
functions for programs.
Inline Mechanism
To understand how the inline
specifier helps to optimize the program, we have to understand the memory model for modern computer programs.
C Memory Model
The memory model for modern computer programs has stack. When a function is called during runtime, some necessary information, such as the return address, local variables, and function arguments, will be allocated and pushed to the stack.
Consider computing a the factorial of $n$ using a recursive function fact
, for each recursion, a fact
function information will be pushed onto the stack.
So when there are too many recursions during runtime and the function does not get return before the program used up the available memory space for stack, we will get “stack overflow” error. Iterative function will not have this kind of problem.
Inline Optimization
The “negative” effect of calling nested functions from the current function is that it introduced overhead time for pointing to the function code, pushing and pop information from the stack. If we only have a single main
function in our program without any auxillary functions, which surely we can write, the program would be “linearly” executed, the overhead time I mentioned above could be eliminated, and thus the program should be faster. However, because there is only one main
function and we could not reuse the code from auxillary function, the code size on the memory could be too big to fit into the memory.
So one way to optimize the program is to reduce the number of push and pop from stack, but not to reduce all of them. The inline
specifier on the function will ask the compiler to “unroll” the nested function and replace the nested function call in the function with the equivalent implementations.
This is very useful for some functions that will call many small functions during runtime. With the inline
of the small functions, the code size on the memory will not grow too large, and the number of push and pop from stack is reduced significantly.
Example
The following example has the library header file foo.h
, the library source file foo.cpp
, the main program source file main.cpp
, and the CMake file CMakeLists.txt
. We would like to make some of the functions or methods inline
.
Code Files
1 | class Foo |
1 |
|
1 |
|
1 | cmake_minimum_required(VERSION 3.13.0) |
Build
1 | $ mkdir build |
Incorrect Inline Method
To make the function inline
, if we add inline
to the beginning of either the function declaration or the function definition in either foo.h
or foo.cpp.
When we compile the program, we would get the linker error:
1 | $ make |
This is a common error for implementations that uses the inline
specifiers.
Correct Inline Method
The correct inline method is to move the definition of the functions that we would like to inline
to the header.
1 | class Foo |
1 |
|
Final Remarks
The inline
specifier is only a suggestion to the compiler and the compiler might just ignore it. The modern compiler might even inline
function for us even if we did not specify inline
at all. I have heard that it is not necessary to use the inline
specifier for conventional programs because the compiler will take care the optimization for us.
References
Inline Specifier Compilation in C/C++