Wednesday, February 16, 2022

Watch out for calling virtual functions of base class

virtual function

 In some heavily invoked code the following construct was used:

#include <string> 
interface ITable
{
   virtual std::string GetData() const = 0;
};

class Table : public ITable
{
public:
   virtual std::string GetData() const override
   {
      return std::string{"1"};
   }
};

class TableCt : public Table
{
public:
   long GetDataCt() const
   {
      const std::string strData = GetData();  // invokes base class through vtable
      return std::stol(strData);
   }
};

 In the real case TableCt wasn't the final class but GetData will not be overriden anywhere else. The code is functional correct but it takes a performance hit since base class function 'GetData' is invoked through vtable and not directly:

    TableCt tbl;
    tbl.GetDataCt();
00007FF653F710BC  lea         rdx,[rsp+30h]  
00007FF653F710C1  lea         rcx,[tbl]  
00007FF653F710C6  call        rax  

  On closer thought this can be explained since the compiler must deal with 'GetData' being overwritten by a derived class. If this is unwanted explicitly invoking the base class function is needed to tell the compiler to suppress the vtable route:

class TableCt : public Table
{
   long GetDataCt() const
   {
      const std::string strData = __super::GetData();  // explicitly invoke base class directly
      return std::stol(strData);
   }
};

 Using the keyword final could also do the trick too although it depends then on the cleverness and optimization of the compiler.

No comments:

Post a Comment

Watch out for hypes in ICT

Hypes  ICT has a rich history of hypes where people thought that this would be a panacea for all problems. These hypes lasted for some time ...