Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optimization of virtual function calls in derived class

What is the best way to call virtual functions in a derived class so that the compiler can inline or otherwise optimize the call?

Example:

class Base {
  virtual void foo() = 0;
};

class Derived: public Base {
  virtual void foo() {...}
  void bar() {
    foo();
  }
};

I want the call to foo() in bar() to always call Derived::foo(). It is my understanding that the call will result in a vtable lookup and the compiler cannot optimize it out since there may be another class inheriting from Derived.

I could explicitly call Derived::foo() but that gets verbose if there are many virtual function calls in Derived. I also find it surprising that I could not find much material online addressing what seems to me to be a common case (a 'final' derived class calling virtual methods) so I wonder if I am misusing virtual functions here or excessively optimizing.

How should this be done? Stop prematurely optimizing and stick with foo(), suck it up and use Derived::foo(), or is there a better way?

like image 848
Eric Langlois Avatar asked May 15 '14 18:05

Eric Langlois


2 Answers

C++11 contains the final keyword, which "specifies that a virtual function can not be overridden in a derived class or that a class cannot be inherited from."1.

It appears that g++ is able to optimize the virtual function call in the derived class if it has been declared final.

I created the following test:

virtualFunctions.h

#pragma once
class Base {
public:
  virtual void foo();
  virtual void bar();
  virtual void baz();
  int fooVar, barVar, bazVar;
};
class Derived: public Base {
public:
  void test();
  virtual void foo();
  virtual void bar();
  virtual void baz() final;
};

virtualFunctions.cpp:

#include "virtualFunctions.h"
void Derived::test() {
  foo();
  Derived::bar();
  baz();
}
void Derived::foo() {
  fooVar = 101;
}
void Derived::bar() {
  barVar = 202;
}
void Derived::baz() {
  bazVar = 303;
}

I am using g++ 4.7.2 and with -O1 the generated assembly contains:

_ZN7Derived4testEv:
.LFB0:
    .loc 1 3 0
    .cfi_startproc
.LVL3:
    pushl   %ebx
.LCFI0:
    .cfi_def_cfa_offset 8
    .cfi_offset 3, -8
    subl    $24, %esp
.LCFI1:
    .cfi_def_cfa_offset 32
    movl    32(%esp), %ebx      ; Load vtable from the stack
    .loc 1 4 0
    movl    (%ebx), %eax        ; Load function pointer from vtable
    movl    %ebx, (%esp)
    call    *(%eax)             ; Call the function pointer
.LVL4:
    .loc 1 5 0
    movl    %ebx, (%esp)
    call    _ZN7Derived3barEv   ; Direct call to Derived::bar()
.LVL5:
    .loc 1 6 0
    movl    %ebx, (%esp)
    call    _ZN7Derived3bazEv   ; Devirtualized call to Derived::baz()

Derived::bar() and Derived::baz() were both called directly, while the vtable was used for foo().

like image 73
Eric Langlois Avatar answered Nov 15 '22 06:11

Eric Langlois


The compiler may be able to optimize it and perform devirtualization if it can statically find out what type is used.

Virtual method calls are quite cheap. Sometime ago I read an article stating that the overhead is roughly ten percent compared to a normal method call. This of course does not consider the missing inlining opportunity.

I also have a feeling that this mixes interface and implementation. I think it would be better to split it into a pure interface and an implementation class.

like image 38
Jens Avatar answered Nov 15 '22 07:11

Jens