Virtual function overhead. How bad is it?

Started by
28 comments, last by GameDev.net 19 years, 1 month ago
In terms of concrete costs:

A vtable read costs one pointer dereference. Just one.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Advertisement
unless your trying to make a real time 3d rendering libaray, operationg system or something like that i wouldn't worry about optimizing on that low of a level, on a game there are far more important thing to spend your time on than being able to rendering one extra polygone per frame
Hi all, since we're talking about virtual methods here, I was wondering, if I have an interface that defines a virtual destructor, if I do this :

class cMyClass : public iMyInterface
{
...
}

iMyInterface *instance = new cMyClass();

delete(instance);


what's the impact of the delete statement? Does it also call the destructor of the inheriting child class first? Will there be an effect on the stack or heap?
You'll never see heaven if you haven't been through hell.
when you create any kind of pointer it starts out at a null value and points to nothing
the new keyword finds some free memory, points the pointer to it and reserves it for the objects exclusive use
the delete keyword does the opposite and frees the memory for other thing to use
calling virtual functions in a tight loop rather than a normal function call can lead to a drop in permormance of as much as 25% or more.

I tested this not too long ago. I'm fairly sure that's about the right number. I do recall it being ridiculously severe.
Quote:Original post by Thrawn80
Hi all, since we're talking about virtual methods here, I was wondering, if I have an interface that defines a virtual destructor, if I do this :

class cMyClass : public iMyInterface
{
...
}

iMyInterface *instance = new cMyClass();

delete(instance);


what's the impact of the delete statement? Does it also call the destructor of the inheriting child class first? Will there be an effect on the stack or heap?


As long as you define the destructor of the base class with virtual, it will be called (if you forget it, only superclass destructor will be called). I did an example of this:

#include <stdio.h>class Interface {public:  Interface() { }  virtual ~Interface() {    printf("Delete from Interface\n");  }};class Subclass : public Interface {public:  Subclass() { }  ~Subclass() {    printf("Delete from Subclass\n");  }};int main() {  Interface* object = new Subclass;  delete object;  return 0;}


And here is the output:

thec@5[temp]$ ./a.outDelete from SubclassDelete from Interface

(btw, that's logical, since you might to use some variables in the superclass that you use in the dsestructor in the object, but that's easy to say after the test, hehe)

Albert
-------------------------------------------http://www.thec.org
Just want to add that using virtual functions defeats inlining. The equivilant non-virtual function may be appropriate to inline and hence be faster.

If you're making appropriate use of virtual functions then there really arent any faster ways to get the same results though.

Just something to be aware of in the design process. And as others have said, the performance hit is negligable to the point where it will make no difference unless they're being called in long, tight loops.

throw table_exception("(? ???)? ? ???");

Quote:Original post by Promit
In terms of concrete costs:

A vtable read costs one pointer dereference. Just one.


Don't forget that it also costs a cache flush/fill.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Quote:Original post by Ravyne
Just want to add that using virtual functions defeats inlining. The equivilant non-virtual function may be appropriate to inline and hence be faster.


Just wanted to clarify that virtual functions can be inlined if the compiler can see the type it is operating on, or if the function is called explicitly:

#include <iostream>struct Base {  inline virtual void Call() {    std::cout << "Base::Call" << std::endl;  }};struct Thing : Base {  inline virtual void Call() {    std::cout << "Thing::Call" << std::endl;  }};struct AnotherThing : Base {  inline virtual void Call() {    Base::Call();//could be inlined as it is calling a specific function  }};


int main() {  Thing thing;  thing.Call();//virtual function call which could be inlined}


I've been thinking about this a bit though recently as I've been looking into templates and generative programming techniques. In the past, in my quest to be object oriented, I've made everything have a virtual interface. So my renderer is virtual, my input devices are virtual, my network connection is virtual, my game world is virtual, my map loader is virtual, etc, etc. I'm writing a general purpose engine which really doesn't need to be so dynamically inter changeable when it comes down to actually making an application.

I'm starting to think that a lot of stuff should be done with templates so that at compile time those objects and types will be tied down to specific things. Of course there are some things which are better off being dynamic but most of the things I mentioned above probably could do without.

Just a thought!
Hello,

Consider this code:
class A{public:  virtual void B1();  void B2();};int main(){  A a;  a.B1();  a.B2();}


VC7 will generate something which look like this:
B1: mov eax, [ecx+4] // get the address of the virtual B1 - ecx is this jmp [eax]        // jump ti this addressmain: mov ecx, &a      // set ecx to this call B1  mov ecx, &a      // set ecx to this call B2        


As you can see, the difference between the two function call is one mov and a jump. While this can be seen as an overhead, the number of wasted cycle is still very low. The difference may be bigger is B2 is inlined (of course).

(edit: quote, edit, quote... these buttons are just too close).

HTH,

This topic is closed to new replies.

Advertisement