Handmade Hero»Forums»Code
Mārtiņš Možeiko
2237 posts / 1 project
Virtual function's disadvantage and real-life examples
Edited by Mārtiņš Možeiko on
Here's an example: https://godbolt.org/z/cKfn94MjP

In function f_derived compiler does not know what d pointer is. Maybe it is some other Derived2 class that inherits Derived and overrides return value of Foo. So it generates indirect branch from vtable (with extra optimizations) - load from vtable happens in lines 5/6 and jump to loaded function pointer in line 12.

But in function f_derived_final compiler sees that df pointer can only be DerivedFinal object in Foo call. Because nobody else can override Foo method even if somebody inherits DerivedFinal. So it simply inlines Foo() call - and no indirect branch happens. Code is simpler and smaller, and executes faster.
78 posts
Virtual function's disadvantage and real-life examples
So that's what inline means! I used to think inline just mean copy the function body then paste it into the caller :)
498 posts
Virtual function's disadvantage and real-life examples
that is effectively what happens, and doing this allows for a bunch of additional optimizations to become possible. This is the more valuable part of inlining.
Miles
127 posts / 4 projects
Virtual function's disadvantage and real-life examples
Another important benefit of inlining, in addition to what ratchetfreak already mentioned, is that you generally want to keep as much information in registers as possible, since registers are the fastest things to access, but when calling a function, the caller has to save registers to the stack before the call and load them back afterward (since it doesn't know how the callee will use the registers), which takes time, increases code size, and gets in the way of optimal register usage. Eliminating this "preamble/postamble" code is a big part of the reason why optimizing compilers pretty much always inline small functions, even where there isn't anything to be gained from other optimizations like constant folding and loop invariant hoisting.
78 posts
Virtual function's disadvantage and real-life examples
So inline functions just use the same registers' information without the need to save it?
Mārtiņš Možeiko
2237 posts / 1 project
Virtual function's disadvantage and real-life examples
Edited by Mārtiņš Možeiko on
When compiler inlines function, it becomes no different than code without function call - just as you said before: "copy&paste" its body. And then compiler sees everything - everything how variables are modified, who writes, who reads to memory, etc.. So it can allocate registers more optimally without need to worry about who can overwrite them (because there is no function called). All that together with additional/better optimizations, of course.
78 posts
Virtual function's disadvantage and real-life examples
So do inline functions have their own call stack? Because if you just do something similar to copy & paste, then what happens when I have some local variables, or return, break, and continue statement inside the function? Do those statements go to the outside scope?
Mārtiņš Možeiko
2237 posts / 1 project
Virtual function's disadvantage and real-life examples
Edited by Mārtiņš Možeiko on
That is kind of wrong question to ask. Each thread executing has its own stack - piece of memory where functions can write whatever they need. Functions don't get their own stack. When compiler compiles function it can decide to put some variables or temporaries on stack of thread who called function. Nothing to do with inlining. Compiler will do to stack whatever things it needs to produce correctly working and optimial code for any function, inlined or not.

if you have functions like this:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int getSum(int len, int* arr)
{
  int sum = 0;
  for (int i=0; i<len; i++) sum += arr[i];
  return sum;
}

int sum100(int* arr)
{
  int s = sum(100, arr);
  return s/5;
}


you can think of inlining process producing function that looks like this:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int sum100_inlined(int* arr)
{
  int s;
  {
    int sum = 0;
    for (int i=0; i<100; i++) sum += arr[i];
    s = sum;
  }
  return s/5;
}


If you'll compile this function and look in asm, you should see exact same thing as for sum100 function.