Monday, December 29, 2008

how slow is dynamic_cast?

I was wondering how slow dynamic_cast really is the other day when I considering designing something in a way that would require it's use. The other alternatives is to have some a virtual functions that performs a cast (requires the parent class to know about all children) or have a virtual function that exposes the type followed by a cast.

One of the first references I found on the web indicated the virtual-function-then-cast approach being up to 20x faster.
http://www.nerdblog.com/2006/12/how-slow-is-dynamiccast.html

That reference didn't provide code, so I wrote a quick test with the hierarchy of following classes:

ClassA, ClassB: public ClassA, and ClassC: public ClassB.

My tests suggest that this approach is about 5x faster when compiled with g++ -O3 (same speed with no optimization). For my application dynamic_cast should be perfectly fine.

Times for 3*100 million cast and function evaluations (in a smidge more than trivial loop)

Not optimized:

mycast 0m13.879s
dynamic_cast 0m0.060s


Optimized

mycast 0m0.919s
dynamic_cast 0m4.729s


a.h

class AClassA{
public:
static const int flag = 0x1;

AClassA(){}
virtual ~AClassA(){}

virtual int name() const ;

int a(){return 1;}
};

class BClassB: public AClassA {
public:
static const int flag = 0x2;

BClassB(){}
virtual ~BClassB(){}

int name() const ;

int b(){return 2;}
};

class CClassC: public BClassB {
public:
static const int flag = 0x4;
CClassC(){}
virtual ~CClassC(){}

int name() const ;

int c(){return 3;}
};


a.cc

#include "a.h"

#ifdef use_mycast
#warning "using mycast"
#define caster mycast
#define extras
#else
#define caster dynamic_cast
#define extras *
#endif

int AClassA::name() const {return AClassA::flag;}

int BClassB::name() const {return AClassA::flag | BClassB::flag;}

int CClassC::name() const {return AClassA::flag | BClassB::flag | CClassC::flag;}

template
T * mycast(AClassA * a){
if(a->name() & T::flag)return (T*)a;
}

int func_dcast(AClassA * a){
CClassC * c = caster(a);
if(c)return c->c();
else {
BClassB * b = caster(a);
if(b)return b->b();
}
return a->a();
}

int main(int ac, char * av[]){
const int ntrials = 100000000;
int sums[8] = {0,0,0,0};

AClassA a;
BClassB b;
CClassC c;

for(int i=0; i!=ntrials; i++){
sums[func_dcast(&a)]++;
sums[func_dcast(&b)]++;
sums[func_dcast(&c)]++;
}
printf("%d %d %d %d\n", sums[0], sums[1], sums[2], sums[3]);
return 0;
}



makefile

all:
g++ a.cc -Duse_mycast -o mycast.opt -O3 -fno-rtti
g++ a.cc -Duse_mycast -o mycast -fno-rtti
g++ a.cc -o dcast.opt -O3
g++ a.cc -o dcast


test.sh

echo "Unoptimized (mycast then dcast)"
time ./mycast | grep real
time ./dcast | grep real
echo "Optimimzed (mycast then dcast)"
time ./mycast.opt | grep real
time ./dcast.opt | grep real

No comments: