Back to Home

novtable optimization

C ++ · msvc · vtable

novtable optimization


The Microsoft compiler allows you to add the "novtable" extension for the "__declspec" attribute when declaring a class.

The stated goal is to significantly reduce the size of the generated code. In experiments with our components, the decrease was from 0.6 to 1.2 percent of the size of the DLL.

Applicability: classes not intended to instantiate directly from them.

For example: purely interface classes.

In code, it looks like this:

struct __declspec(novtable) IDrawable
{
	virtual void Draw() const = 0;
};

Note: the struct keyword was used to declare an interface class to rid the example of irrelevant article details; whereas in the case of using a class, one would have to use public to indicate the "publicity" of methods. For the same reason, I will not add a virtual destructor to the interface class in this article.

The name "novtable" promises that there will be no virtual table ... But how does the mechanism for invoking virtual functions work in the following code:

// Добавим декларацию прямоугольника, реализующего интерфейс IDrawable:
class Rectangle : public IDrawable
{
	virtual void Draw() const override
	{
	}
	int width;
	int height;
};
…
IDrawable* drawable = new Rectangle;
drawable->Draw(); // происходит вызов Rectangle::Draw
…


Recall what is added when declaring a virtual function in a class:

  1. Defining a table of virtual functions. One instance of this table is used for all instances of the class.
  2. A pointer to the virtual function table is added to the class data members.
  3. The code to initialize this pointer in the constructor of the class.

Thus, in our example, there will be a declaration of two virtual function tables: for IDrawable and for Rectangle. When creating a Rectangle object, the IDrawable constructor is the first to execute, which initializes a pointer to its virtual function table. Schematically, it looks like this:


Since the draw function in IDrawable is declared pure virtual (the "= 0" is indicated instead of the function body), the address of the purecall function generated by the compiler is written in the table of virtual functions.

Then the Rectangle constructor is executed, which initializes the same pointer, but to its virtual function table:



What does “novtable” do, and why does Microsoft promise to reduce the size of the code?


It is the unnecessary definition of the IDrawable virtual function table and initialization of the pointer to it in the IDrawable constructor that are excluded from the resulting code when adding “novtable”.

In this case, when constructing an IDrawable, the pointer to the virtual function table will contain an unpredictable value. But this should not bother us, since creating an implementation with access to virtual functions before the complete construction of the object is usually a mistake. If, for example, a non-virtual function of this class is called in the constructor of the base class, which in turn calls a virtual function, then the purecall function will be called without novtable, and unpredictable behavior with novtable; none of the options may be acceptable.

Note that there is not only a decrease in size, but also some acceleration of the program.

RTTI


As you know, std :: dynamic_cast allows you to cast pointers and links from one instance of a class to a pointer and a link to another, if these classes are hierarchically linked and polymorphic (contain a table of virtual functions). In turn, the typeid operator allows you to get information about an object in runtime using the pointer (link) to that object passed to it. These capabilities are provided by the RTTI mechanism, which uses type information located with reference to the vtable of the class. The details of the structure and location depend on the compiler. In the case of the Microsoft compiler, it looks like this:



Therefore, if the compiler is ordered to enable RTTI during assembly, novtable also excludes the creation of the type_info definition for IDrawable and the service data required for it.
Note that if you somehow provide the knowledge that a referenced pointer (link) to a base class indicates an implementation of a derivative, then std :: static_cast is more efficient and does not require RTTI.

Microsoft specific


In addition to MSVC, this feature with the same syntax is present in Clang when compiling under Windows.

conclusions


  1. __declspec (novtable) - does not affect the amount of memory occupied by class instances.
  2. Reducing the size and speeding up the program is ensured by eliminating the definition of unused virtual function tables, RTTI overhead, and eliminating the initialization code for the pointer to the virtual function table in the interface class constructors.

Read Next