How Multiple Levels of Inheritance Affect Virtual Function Call Overhead in C++

cfunctionspolymorphism

I am considering using a class hierarchy with more than a single level of inheritance, where virtual member functions form a "chain", for example:

struct Base
{ virtual void foo(); };

struct D1 : Base
{ virtual void foo(); };

struct D2 : D1
{ void foo(); };

However I would like to know before using similar code in practice if this would cause additional dynamic dispatch overhead when calling trough a base class pointer. Consider the following example:

D2 instance;
D1* d1ptr = &instance;
Base* baseptr = &instance;

//normal function call to "D2::foo()", no dynamic dispatch.
instance.foo();
//virtual function call to "D2::foo()", one dynamic dispatch.
d1ptr.foo(); 
//virtual function call to "D2::foo()", does this incur additional
//..overhead compared to "d1ptr.foo()"?
baseptr.foo();

Best Answer

In your example, the depth of the inheritance tree does not affect performance. The reason is simple, every instance has its pointer to some vtable (containing function pointers), and a virtual function call goes just thru that vtable.

In other words, your code works like the following C code:

struct vtable {
   void (*fooptr) (struct D1*);
};

struct D1 {
   const struct vtable*vptr;
};

struct D2 {
   const struct vtable*vptr;
};

And e.g. baseptr->foo() is "transformed" at compilation time to baseptr->vptr.fooptr(baseptr) (and of course d1ptr->foo() is "transformed" to d1ptr->vptr.fooptr(d1ptr) etc...)

So the execution cost is the same: get the vtable, and call indirectly the function pointer inside. The cost remains the same even if you had a struct D4 subclass of struct D3 subclass of struct D2 subclass of struct D1. You could have a struct D99 with 99 inheritances and the execution cost would remain the same.

On some processors, any indirect call may be slower than a direct call, e.g. because of branch predictor cost.

The data of the vtable themselves is built at compilation time (as constant static data).

Things become a bit more complex with virtual inheritance and/or multiple inheritance

BTW, some C object frameworks (e.g. GObject from Gtk, or several data structures inside the Linux kernel) also provide their vtable (or class data). Pointing to a data structure containing function pointers is quite common (even in C). Read also about closures and you'll see a relation with "vtables" (both closures and objects are mixing code with data).

Some C++ compilers (e.g. GCC) may provide devirtualization as an optimization technique.