Aussie AI

Avoid unnecessary virtual function calls

  • Book Excerpt from "Generative AI in C++"
  • by David Spuler, Ph.D.

Avoid unnecessary virtual function calls

The use of virtual functions, when they are not needed, is obviously inefficient. virtual functions are needed only when dealing with pointers or references to objects of unknown type. If the program never uses pointers or references to objects, or if it does not have any derived classes, no function needs to be virtual and the use of virtual wastes space. In addition, because virtual functions relate only to the use of derived classes, declaring any functions as virtual in a class that has no derived classes is also unnecessarily inefficient.

One common situation where virtual may appear necessary, but need not be, occurs with redefining a member function in a derived class. This does not necessarily mean that the function must be defined as virtual in the base class (nor in the derived class — the virtual keyword is never needed in the derived class). Of course, if the program starts using pointers or references to these classes, the functions may need to be virtual, in which case it may be better style to declare the member function as virtual.

A call to a virtual function need not always be a “real” virtual call. For example, passing an object by reference (either as a reference or as a pointer type) can occur when changing functions to pass-by-reference for efficiency improvement. Any calls to virtual functions inside that (not necessarily virtual) function will be such that the compiler cannot know that an ordinary function call to the member function would suffice. It does not perform any global analysis to determine that all arguments to the function are base objects, and not derived objects. For example, in the following code, it isn’t clear that the call to the (virtual) print function could be replaced by an ordinary call:

    void print_base_object( Base & object)
    {
        object.print();
    }

The overhead of virtual function calls can be removed whenever the programmer can be sure that only one type of pointer/reference to an object is being used. In particular, whenever a programmer can be sure that a pointer/reference to a base class object points to a particular object, the qualified member function name can be used. For example, the virtual call uses:

    p->print();

And the more efficient code that avoids a virtual function call is:

    p->Base::print();

An example of extra information making this change possible occurs when a program uses a number of different (homogeneous) linked lists, with each linked list containing the same type of object (one with base objects, one with derived objects). When implementing a print_list function to print out a linked list, you can write it generally to call a virtual-declared print_object function:

    void LinkedList::print_list()
    {
        for (Base *temp = head; temp != NULL; temp = temp->next())
            temp->print_object();
    }

This means that each call to print_object has the run-time overhead of a virtual function call. A more efficient alternative is to make use of the knowledge that each list must contain the same type of object, and have two different print_list functions (i.e. use a virtual function to do the dirty work of printing the objects).

    void Base::print_list_hidden()
    {
        for (Base *temp = this; temp != NULL; temp = temp->next())
        temp->Base::print_object();
    }

    void Derived::print_list_hidden()
    {
        for (Derived *temp = this; temp != NULL;
        temp = (Derived*)temp->next())
        temp->Derived::print_object();
    }

    void LinkedList::print_list()
    {
        if (head != NULL)
            head->print_list_hidden(); // call virtual function
    }

With this approach, all of the lower-level calls to print_object can be bound at compile-time and the only virtual call is the call to print_list_hidden at the very top. Hence, by using our knowledge about the linked lists, we have reduced the number of run-time virtual function calls.

 

Next:

Up: Table of Contents

Buy: Generative AI in C++: Coding Transformers and LLMs

Generative AI in C++ The new AI programming book by Aussie AI co-founders:
  • AI coding in C++
  • Transformer engine speedups
  • LLM models
  • Phone and desktop AI
  • Code examples
  • Research citations

Get your copy from Amazon: Generative AI in C++