Vous êtes sur la page 1sur 9

C++ vs C Performance Comparison (Virtual Functions, Inheritance an... http://www.eventhelix.com/RealtimeMantra/Basics/ComparingCPPA...

Home | EventStudio System Designer 4.0 | VisualEther Protocol Analyzer 1.0 | Real-time Mantra | Contact Us
Home > Real-time Mantra > Object Oriented Design > C++ and C (Inheritance and Virtual Functions)

Comparing C++ and C (Inheritance and Virtual


Functions)
We have covered C++ and C performance in a previous article. In this article we will dig deeper into C++
performance by analyzing the overhead of inheritance and virtual functions.

Virtual Functions and Inheritance


This section presents the C++ code for a typical virtual function invocation scenario. This is then compared to
the equivalent C code.

C++ Code
// A typical example of inheritance and
virtual function use.
// We would be mapping this code to
equivalent C.

// Prototype graphics library function to


draw a circle
void glib_draw_circle (int x, int y, int
radius);

// Shape base class declaration


class Shape
{
protected:
int m_x; // X coordinate
int m_y; // Y coordinate

public:
// Pure virtual function for drawing
virtual void Draw() = 0;

// A regular virtual function


virtual void MoveTo(int newX, int newY);

// Regular method, not overridable.


void Erase();

// Constructor for Shape


Shape(int x, int y);

// Virtual destructor for Shape


virtual ~Shape();
};

// Circle class declaration


class Circle : public Shape
{
private:
int m_radius; // Radius of the circle

public:
// Override to draw a circle
virtual void Draw();

// Constructor for Circle


Circle(int x, int y, int radius);

// Destructor for Circle


virtual ~Circle();

1 of 9 17/3/2008 12:59 µµ
C++ vs C Performance Comparison (Virtual Functions, Inheritance an... http://www.eventhelix.com/RealtimeMantra/Basics/ComparingCPPA...

};

// Shape constructor implementation


Shape::Shape(int x, int y)
{
m_x = x;
m_y = y;
}

// Shape destructor implementation


Shape::~Shape()
{
//...
}

// Circle constructor implementation


Circle::Circle(int x, int y, int radius) :
Shape (x, y)
{
m_radius = radius;
}

// Circle destructor implementation


Circle::~Circle()
{
//...
}

// Circle override of the pure virtual Draw


method.
void Circle::Draw()
{
glib_draw_circle(m_x, m_y, m_radius);
}

main()
{
// Define a circle with a center at
(50,100) and a radius of 25
Shape *pShape = new Circle(50, 100, 25);

// Define a circle with a center at (5,5)


and a radius of 2
Circle aCircle(5,5, 2);

// Various operations on a Circle via a


Shape pointer
pShape->Draw();
pShape->MoveTo(100, 100);
pShape->Erase();
delete pShape;

// Invoking the Draw method directly


aCircle.Draw();
}

C code implementing the above C++ functionality is shown below. The code also includes compiler generated
constructs like vtables. Virtual function access using vtables is also covered. (The presentation here has been
simplified here to aid understanding).

C Code
/*
The following code maps the C++ code for the
Shape and Circle classes
to C code.
*/

#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef int BOOLEAN;

2 of 9 17/3/2008 12:59 µµ
C++ vs C Performance Comparison (Virtual Functions, Inheritance an... http://www.eventhelix.com/RealtimeMantra/Basics/ComparingCPPA...

/*
Error handler used to stuff dummy VTable
entries. This is covered later.
*/
void pure_virtual_called_error_handler();

/* Prototype graphics library function to draw a


circle */
void glib_draw_circle (int x, int y, int
radius);

typedef void (*VirtualFunctionPointer)(...);

/*
VTable structure used by the compiler to keep
track of the virtual functions associated with a
class.
There is one instance of a VTable for every
class
containing virtual functions. All instances of
a given class point to the same VTable.
*/
struct VTable
{
/*
d and i fields are used when multiple
inheritance and virtual
base classes are involved. We will be
ignoring them for this
discussion.
*/
int d;
int i;

/*
A function pointer to the virtual function to
be called is
stored here.
*/
VirtualFunctionPointer pFunc;
};

/*
The Shape class maps into the Shape structure in
C. All
the member variables present in the class are
included
as structure elements. Since Shape contains a
virtual
function, a pointer to the VTable has also been
added.
*/

struct Shape
{
int m_x;
int m_y;

/*
The C++ compiler inserts an extra pointer to a
vtable which
will keep a function pointer to the virtual
function that
should be called.
*/
VTable *pVTable;
};

/*
Function prototypes that correspond to the C++
methods
for the Shape class,
*/

3 of 9 17/3/2008 12:59 µµ
C++ vs C Performance Comparison (Virtual Functions, Inheritance an... http://www.eventhelix.com/RealtimeMantra/Basics/ComparingCPPA...

Shape *Shape_Constructor(Shape *this_ptr, int x,


int y);
void Shape_Destructor(Shape *this_ptr, bool
dynamic);
void Shape_MoveTo(Shape *this_ptr, int newX, int
newY);
void Shape_Erase(Shape *this_ptr);

/*
The Shape vtable array contains entries for Draw
and MoveTo
virtual functions. Notice that there is no entry
for Erase,
as it is not virtual. Also, the first two fields
for every
vtable entry are zero, these fields might have
non zero
values with multiple inheritance, virtual base
classes
A third entry has also been defined for the
virtual destructor
*/

VTable VTableArrayForShape[] =
{
/*
Vtable entry virtual function Draw.
Since Draw is pure virtual, this entry
should never be invoked, so call error
handler
*/
{ 0, 0, (VirtualFunctionPointer)
pure_virtual_called_error_handler },

/*
This vtable entry invokes the base class's
MoveTo method.
*/
{ 0, 0, (VirtualFunctionPointer)
Shape_MoveTo },

/* Entry for the virtual destructor */


{ 0, 0, (VirtualFunctionPointer)
Shape_Destructor }
};

/*
The struct Circle maps to the Circle class in
the C++ code.
The layout of the structure is:
- Member variables inherited from the the base
class Shape.
- Vtable pointer for the class.
- Member variables added by the inheriting class
Circle.
*/

struct Circle
{
/* Fields inherited from Shape */
int m_x;
int m_y;
VTable *pVTable;

/* Fields added by Circle */


int m_radius;
};

/*
Function prototypes for methods in the Circle
class.
*/

Circle *Circle_Constructor(Circle *this_ptr, int

4 of 9 17/3/2008 12:59 µµ
C++ vs C Performance Comparison (Virtual Functions, Inheritance an... http://www.eventhelix.com/RealtimeMantra/Basics/ComparingCPPA...

x, int y, int radius);


void Circle_Draw(Circle *this_ptr);
void Circle_Destructor(Circle *this_ptr, BOOLEAN
dynamic);

/* Vtable array for Circle */

VTable VTableArrayForCircle[] =
{
/*
Vtable entry virtual function Draw.
Circle_Draw method will be invoked when
Shape's
Draw method is invoked
*/
{ 0, 0, (VirtualFunctionPointer) Circle_Draw
},

/*
This vtable entry invokes the base class's
MoveTo method.
*/
{ 0, 0, (VirtualFunctionPointer)
Shape_MoveTo },

/* Entry for the virtual destructor */


{ 0, 0, (VirtualFunctionPointer)
Circle_Destructor }
};

Shape *Shape_Constructor(Shape *this_ptr, int x,


int y)
{
/* Check if memory has been allocated for
struct Shape. */
if (this_ptr == NULL)
{
/* Allocate memory of size Shape. */
this_ptr = (Shape *) malloc(sizeof(Shape));
}

/*
Once the memory has been allocated for Shape,
initialise members of Shape.
*/
if (this_ptr)
{
/* Initialize the VTable pointer to point to
shape */
this_ptr->pVTable = VTableArrayForShape;
this_ptr->m_x = x;
this_ptr->m_y = y;
}

return this_ptr;
}

void Shape_Destructor(Shape *this_ptr, BOOLEAN


dynamic)
{
/*
Restore the VTable to that for Shape. This is
required so that the destructor does not
invoke
a virtual function defined by a inheriting
class.
(The base class destructor is invoked after
inheriting
class actions have been completed. Thus it is
not
safe to invoke the ineriting class methods
from the
base class destructor)
*/

5 of 9 17/3/2008 12:59 µµ
C++ vs C Performance Comparison (Virtual Functions, Inheritance an... http://www.eventhelix.com/RealtimeMantra/Basics/ComparingCPPA...

this_ptr->pVTable = VTableArrayForShape;

/*...*/

/*
If the memory was dynamically allocated
for Shape, explicitly free it.
*/
if (dynamic)
{
free(this_ptr);
}
}

Circle *Circle_Constructor(Circle *this_ptr, int


x, int y, int radius)
{
/* Check if memory has been allocated for
struct Circle. */
if (this_ptr == NULL)
{
/* Allocate memory of size Circle. */
this_ptr = (Circle *)
malloc(sizeof(Circle));
}

/*
Once the memory has been allocated for Circle,
initialise members of Circle.
*/
if (this_ptr)
{
/* Invoking the base class constructor */
Shape_Constructor((Shape *)this_ptr, x,
y);
this_ptr->pVTable = VTableArrayForCircle;

this_ptr->m_radius = radius;
}
return this_ptr;
}

void Circle_Destructor(Circle *this_ptr, BOOLEAN


dynamic)
{
/* Restore the VTable to that for Circle */
this_ptr->pVTable = VTableArrayForCircle;

/*...*/

/*
Invoke the base class destructor after
ineriting class
destructor actions have been completed. Also
note that
that the dynamic flag is set to false so that
the shape
destructor does not free any memory.
*/
Shape_Destructor((Shape *) this_ptr, FALSE);

/*
If the memory was dynamically allocated
for Circle, explicitly free it.
*/
if (dynamic)
{
free(this_ptr);
}
}

void Circle_Draw(Circle *this_ptr)


{
glib_draw_circle(this_ptr->m_x,

6 of 9 17/3/2008 12:59 µµ
C++ vs C Performance Comparison (Virtual Functions, Inheritance an... http://www.eventhelix.com/RealtimeMantra/Basics/ComparingCPPA...

this_ptr->m_y, this_ptr->m_radius);
}

main()
{
/*
Dynamically allocate memory by passing NULL in
this arguement.
Also initialse members of struct pointed to by
pShape.
*/
Shape *pShape = (Shape *)
Circle_Constructor(NULL, 50, 100, 25);

/* Define a local variable aCircle of type


struct Circle. */
Circle aCircle;

/* Initialise members of struct variable


aCircle. */
Circle_Constructor(&aCircle, 5, 5, 2);

/*
Virtual function Draw is called for the shape
pointer. The compiler
has allocated 0 offset array entry to the Draw
virtual function.
This code corresponds to "pShape->Draw();"
*/
(pShape->pVTable[0].pFunc)(pShape);

/*
Virtual function MoveTo is called for the
shape pointer. The compiler
has allocared 1 offset array entry to the
MoveTo virtual function.
This code corresponds to "pShape->MoveTo(100,
100);"
*/
(pShape->pVTable[1].pFunc)(pShape, 100, 100);

/*
The following code represents the Erase
method. This method is
not virtual and it is only defined in the base
class. Thus
the Shape_Erase C function is called.
*/
Shape_Erase(pShape);

/* Delete memory pointed to by pShape


(explicit delete in original code).
Since the destructor is declared virtual, the
compiler has allocated
2 offset entry to the virtual destructor
This code corresponds to "delete pShape;".
*/
(pShape->pVTable[2].pFunc)(pShape, TRUE);

/*
The following code corresponds to
aCircle.Draw().
Here the compiler can invoke the method
directly instead of
going through the vtable, since the type of
aCircle is fully
known. (This is very much compiler dependent.
Dumb compilers will
still invoke the method through the vtable).
*/
Circle_Draw(&aCircle);

/*
Since memory was allocated from the stack for

7 of 9 17/3/2008 12:59 µµ
C++ vs C Performance Comparison (Virtual Functions, Inheritance an... http://www.eventhelix.com/RealtimeMantra/Basics/ComparingCPPA...

local struct
variable aCircle, it will be deallocated when
aCircle goes out of scope.
The destructor will also be invoked. Notice
that dynamic flag is set to
false so that the destructor does not try to
free memory. Again, the
compiler does not need to go through the
vtable to invoke the destructor.
*/
Circle_Destructor(&aCircle, FALSE);
}

Analysis
This section analyses the C++ code and its C translation and identifies the performance impact.

Object Construction Inheritance does increase the object construction overhead, as constructors for all the
parent classes in the class hierarchy are invoked. There is the additional overhead of
setting up vtable in the constructor.

Object Destruction Inheritance does increase the object destruction overhead, as destructors for all the
parent classes in the class hierarchy are invoked. There is the additional overhead of
setting up vtable in the destructor. Virtual destructors also increase the overhead of
object destruction.

Virtual Function Virtual function invocation is slightly more expensive than invoking a function through a
Invocation function pointer. In many scenarios, intelligent compilers can use normal method
invocation instead of a virtual function invocation.

In a well designed object oriented system, a virtual function call would typically replace a
switch statement so virtual function invocation might actually be faster than conventional
coding techniques. For example, a generic draw statement in a "C" based paint program
would involve switching over the type of shape and then invoking the corresponding draw
function. In C++, this logic will be replaced by a virtual function call.

In our experience we have found that poorly designed and excessive use of constructors
and destructors reduces performance much more than virtual function calls.

Memory Overhead Using plain inheritance has no memory overhead. Inheritance with virtual functions
however does introduce the following memory overhead:

A vtable array pointer is added to all classes that use virtual functions.
Global vtable arrays are declared for every call with virtual functions (this should be a
very small overhead).

Locality of In the current computing environment, processor speeds have increased considerably
Reference but memory access speeds haven't kept pace. In such a scenario, cache hit ratio of an
application plays a very important role in determining application performance.

In general this turns out to be an advantage for programs written in C++. With C++ code
and data locality of reference is much better than C, as all the class code manipulating
object data is located together. Also all object data is located at one place. In C code and
data are scattered all over the place. Thus a C++ program should offer a better locality of
reference than a C program. In many cases this might more than compensate for the
performance overhead of C++.

8 of 9 17/3/2008 12:59 µµ
C++ vs C Performance Comparison (Virtual Functions, Inheritance an... http://www.eventhelix.com/RealtimeMantra/Basics/ComparingCPPA...

Multiple Inheritance In this article we have not covered multiple inheritance and virtual base classes. There is
and Virtual Base a significant increase in overhead due to these features. We would recommend that you
Classes stay away from using these features.

Explore More
Compare C++ and the equivalent C code for class declarations and method invocation.
Object Oriented Programming in C
C to Assembly Code Translation.
Home | EventStudio System Designer 4.0 | VisualEther Protocol Analyzer 1.0 | Real-time Mantra | Contact Us
Copyright © 2000-2007 EventHelix.com Inc. All Rights Reserved.

9 of 9 17/3/2008 12:59 µµ

Vous aimerez peut-être aussi