typeid() faster than dynamic_cast<>

J

Jamie Burns

Hello,

I just did a simple benchmark:

for (xx=0;xx<100000;xx++) {
rDerived* derived = dynamic_cast<rDerived*>(object);
if (derived) derived->setValue(message.data.messageSetInt.value);
}

against:

for (xx=0;xx<100000;xx++) {
if (typeid(object) == typeid(rDerived*)) ((rDerived*)
object)->setValue(message.data.messageSetInt.value);
}

And the latter case blew the former out of the water. Using typeid() with a
C style cast was 94 times faster than using dynamic_cast<>.

So is it really better to use typeid() and a C style cast rather than the
(apparantly) slower dynamic_cast<>?

Jamie Burns.
 
A

Alf P. Steinbach

I just did a simple benchmark:

for (xx=0;xx<100000;xx++) {
rDerived* derived = dynamic_cast<rDerived*>(object);
if (derived) derived->setValue(message.data.messageSetInt.value);
}

against:

for (xx=0;xx<100000;xx++) {
if (typeid(object) == typeid(rDerived*)) ((rDerived*)
object)->setValue(message.data.messageSetInt.value);
}

And the latter case blew the former out of the water. Using typeid() with a
C style cast was 94 times faster than using dynamic_cast<>.

In general this is a quality-of-implementation issue. A factor of 94
for this code seems excessive. To say the least.

So is it really better to use typeid() and a C style cast rather than the
(apparantly) slower dynamic_cast<>?

Assuming you meant to write 'static_cast', not 'C style cast', which you
should never use:

It depends. With dynamic_cast you allow a wider range of actual types for
'object' than you do with typeid. With typeid you tell the compiler that
you're only interested in one particular type, and so it can be faster,
as well as more specifically expressing the intent of the code.

Btw., why not use '++xx', and why not declare 'xx' in the loop?
 
L

lilburne

Jamie said:
And the latter case blew the former out of the water. Using typeid() with a
C style cast was 94 times faster than using dynamic_cast<>.

So is it really better to use typeid() and a C style cast rather than the
(apparantly) slower dynamic_cast<>?

What if rDerived is actually sDerived, which version is
correct then?
 
P

Pete Becker

Jamie said:
So is it really better to use typeid() and a C style cast rather than the
(apparantly) slower dynamic_cast<>?

They do different things. When you compare typeids you're only making
one comparison. When you use dynamic_cast you're walking the inheritance
hierarchy looking for a type match. Of course, the benefit of the latter
is that it actually gets the right answer if you change your inheritance
hierarchy in various ways.

Using typeid is only "better" if you don't care how robust the code is
and you've determined that the type comparison is a bottleneck in your
application.
 
J

Jonathan Turkanis

Jamie Burns said:
Hello,

I just did a simple benchmark:

for (xx=0;xx<100000;xx++) {
rDerived* derived = dynamic_cast<rDerived*>(object);
if (derived) derived->setValue(message.data.messageSetInt.value);
}

against:

for (xx=0;xx<100000;xx++) {
if (typeid(object) == typeid(rDerived*)) ((rDerived*)
object)->setValue(message.data.messageSetInt.value);
}

I think you might be making a simple mistake. A pointer is not a
polymorphic type, so typeid(T*) returns a type_info object
representing the static pointer type T*. An expression of the form
typeid(rDerived*) returns a type_info object represent the type
rDerived*. Likewise, if 'object' is decalred:

Base* object;

then typeid(object) should return a type_info object representing the
type Base*.

The condition in the second loop can be evaluated partly at compile
time, since the static types are known. If 'object' is decalred as
above, the condition will never hold, and the function calls will be
skipped. Have you verified that the function setValue is actually
being called?

Jonathan
 
D

David White

Jamie Burns said:
Hello,

I just did a simple benchmark:

for (xx=0;xx<100000;xx++) {
rDerived* derived = dynamic_cast<rDerived*>(object);
if (derived) derived->setValue(message.data.messageSetInt.value);
}

against:

for (xx=0;xx<100000;xx++) {
if (typeid(object) == typeid(rDerived*)) ((rDerived*)
object)->setValue(message.data.messageSetInt.value);
}

And the latter case blew the former out of the water. Using typeid() with a
C style cast was 94 times faster than using dynamic_cast<>.

In a similar benchmark using VC++ 7.0 optimized for speed, I found the
dynamic_cast version to take between 3 and 4 times as long as the typeid
version. This is not surprising considering what they each do, but a factor
of 94 is a surprise.
So is it really better to use typeid() and a C style cast rather than the
(apparantly) slower dynamic_cast<>?

No. They do different things. They are interchangeable only if there are no
classes derived from rDerived, or you want to ignore them (unlikely, since a
class derived from rDerived IS-A rDerived). Even if there are no classes
derived from rDerived now, there might be later, and you wouldn't want your
code to break. Unless you really need to squeeze every drop of speed out of
the machine, using typeid for this purpose is a bad move.

DW
 
A

Andrey Tarasevich

Jonathan said:
I think you might be making a simple mistake. A pointer is not a
polymorphic type, so typeid(T*) returns a type_info object
representing the static pointer type T*. An expression of the form
typeid(rDerived*) returns a type_info object represent the type
rDerived*. Likewise, if 'object' is decalred:

Base* object;

then typeid(object) should return a type_info object representing the
type Base*.
...

Nice catch. It appears that no one else noticed this. The second test
('typeid') does not do what OP intended it to do. The 'if' statement in
the second loop should look as follows

...
if (typeid(*object) == typeid(rDerived))
...
 
P

Pete Becker

Andrey said:
Nice catch. It appears that no one else noticed this. The second test
('typeid') does not do what OP intended it to do. The 'if' statement in
the second loop should look as follows

...
if (typeid(*object) == typeid(rDerived))
...

Of course, given the number of typos in the "code", it's not really
possible to tell whether that's what he did.
 
H

Hendrik Belitz

Jamie said:
So is it really better to use typeid() and a C style cast rather than the
(apparantly) slower dynamic_cast<>?

The best way is not to use that stuff at all. Ask yourself if your software
design is really good. Most of the time using the RTTI functionality is
only a hint to a bad program design. AFAIK there is only one useful
application of RTTI, and that's emulating weak typing in C++.
 
L

lilburne

Hendrik said:
Jamie Burns wrote:




The best way is not to use that stuff at all. Ask yourself if your software
design is really good. Most of the time using the RTTI functionality is
only a hint to a bad program design. AFAIK there is only one useful
application of RTTI, and that's emulating weak typing in C++.

Not quite! Lets say I have a class Curve and derived classes
representing line, circular arc, bezier, nurb, composites. And given two
Curves I want to find the points where they intersect. Now I can write
a general method for this, but if I know (RTTI) that the both entities
are lines, or both are arcs, then I can provide a more efficent solution
for these special cases.
 
K

Klaus Nowikow

Hendrik said:
Jamie Burns wrote:




The best way is not to use that stuff at all. Ask yourself if your software
design is really good. Most of the time using the RTTI functionality is
only a hint to a bad program design. AFAIK there is only one useful
application of RTTI, and that's emulating weak typing in C++.

Well, I don't think dynamic_cast<>ing from one abstract base class
to another abstract base class (interface) is the bad thing.
It's the _downcast_ that shows poor class design.
 
N

Nick Hounsome

lilburne said:
Not quite! Lets say I have a class Curve and derived classes
representing line, circular arc, bezier, nurb, composites. And given two
Curves I want to find the points where they intersect. Now I can write
a general method for this, but if I know (RTTI) that the both entities
are lines, or both are arcs, then I can provide a more efficent solution
for these special cases.

How about double dispatch - if you're hierarchy is fixed then this is the
cleanest way.

class Curve
{
protected:
// derived classes override these with efficient impl
virtual XXX intersect2(const Line&) const = 0;
virtual XXX intersect2(const Bezier&) const = 0;
//etc etc
public:
// all the derived classes implement intersect EXACTLY the same
virtual XXX intersect(const Curve& other) const
{
return other.intersect2(*this);
}
};

class Bezier : public Curve { .....}
etc

Bezier bez;
Line line;
Curve& c1 = bez;
Curve& c2 = line;
c1.intersect(c2);
calls Bezier::intersect(const Curve&)
calls Line::intersect2(const Bezier&)
does efficient intersection
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top