Wednesday, July 7, 2010

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



Consider the following call to function g():

void f()
{
int x =
/*...*/;
int y =
/*...*/;
int z =
/*...*/;
...code that uses x, y and z...
g(x, y, z);
...more code that uses x, y 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 x, y 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.