5

What happens when you add a first virtual function to a class in terms of its memory consumption, object layout, and so on? How much slower are virtual functions than normal functions?

ltnewberry
  • 88
  • 5
  • 2
    http://stackoverflow.com/questions/449827/virtual-functions-and-performance-c – Alan Stokes May 01 '16 at 22:13
  • 2
    http://stackoverflow.com/questions/99297/how-are-virtual-functions-and-vtable-implemented – Alan Stokes May 01 '16 at 22:14
  • What an excellent suggestion you've made: to create a few simple programs that can be benchmarked to determine the overhead of virtual classes. Be sure to edit your question to include the results of your benchmarking. – Sam Varshavchik May 01 '16 at 22:14
  • Thanks for the links! I'm new to Stack Overflow. I tried searching the question but apparently didn't search hard enough! @AlanStokes – ltnewberry May 01 '16 at 22:16
  • 1
    Long story short: the first virtual function adds a pointer to the vtable as an extra hidden member. On current processors, if you keep calling the same virtual function in a loop on objects of the same type there's essentially no performance penalty compared to a "regular" function call, since the branch predictor quickly picks up the pattern. The big performance impact of virtual functions is generally in the missed opportunity for inlining. – Matteo Italia May 01 '16 at 23:17

1 Answers1

2

If you really care I suggest testing it on your own compiler & system, as it may produce different results. Here is an example I used:

#include <iostream>

template<typename B> struct Base { B b; void f() {}; };
template<typename B> struct Base_Virtual { B b; virtual void f() {}; };
template<typename B> struct Base_Pointer { B b; void* p; };

#define PRINT_ALIGNOF_SIZEOF(T) std::cout << "sizeof(" #T ") = " << sizeof(T) << ", alignof(" #T ") = " << alignof(T) << std::endl
int main()
{
    PRINT_ALIGNOF_SIZEOF(char);
    PRINT_ALIGNOF_SIZEOF(Base<char>);
    PRINT_ALIGNOF_SIZEOF(Base_Virtual<char>);
    PRINT_ALIGNOF_SIZEOF(Base_Pointer<char>);
    std::cout << std::endl;
    PRINT_ALIGNOF_SIZEOF(void*);
    PRINT_ALIGNOF_SIZEOF(Base<void*>);
    PRINT_ALIGNOF_SIZEOF(Base_Virtual<void*>);
    PRINT_ALIGNOF_SIZEOF(Base_Pointer<void*>);

    std::cin.ignore();
}   

Which produced the following output (with Windows 10 / Visual C++ 14.0 compiled as an ‘x64’ program)

sizeof(char) = 1, alignof(char) = 1
sizeof(Base<char>) = 1, alignof(Base<char>) = 1
sizeof(Base_Virtual<char>) = 16, alignof(Base_Virtual<char>) = 8
sizeof(Base_Pointer<char>) = 16, alignof(Base_Pointer<char>) = 8

sizeof(void*) = 8, alignof(void*) = 8
sizeof(Base<void*>) = 8, alignof(Base<void*>) = 8
sizeof(Base_Virtual<void*>) = 16, alignof(Base_Virtual<void*>) = 8
sizeof(Base_Pointer<void*>) = 16, alignof(Base_Pointer<void*>) = 8

This suggests that adding a virtual function to a class is equivalent to adding a void* member at the end. (Note: adding more virtual functions did not change the result).

As a general rule of thumb, only add virtual functions if they are useful (though adding a virtual destructor is considered best practice if your class is going to be used in hierarchies, even if it is always trivial).

Isaac
  • 816
  • 5
  • 12