performance of typeid(..) comparison

9569bfe5ca7c477bc1e6d9587671bb75
0
kulik 101 Oct 12, 2006 at 16:28

I am in the phase of redesigning the shader system of my engine.
I designed new shiny metaprogrammed shader parameters class that will hold all the parameters (uniforms in GLSL). These parameters are later passed to GLSL. However every shading language can support numerous data types and I don’t want to enumerate them in ShaderParameters and use the enum for comparison later. I want user to pass anything he/she wants and let the implementation deal with it. So I thought of a way, what about making a map of boost::any values. These values would hold the uniform value, while the key of the map is uniform’s name. In GLSL implementation I would just compare the typeid’s of types supported by GLSL to type in boost::any and if they match, I would cast the boost::any and bind it to GLSL.

However I am not quite sure this would perform well. And also it doesn’t work with std::vector (I can’t detect them or cast them, but if I write their typeid to std::cout - via it’s name() method, it shows std::vector<float, std::allocator<float> > when I demangle it. This is really weird and I can’t figure out why doesn’t it work.

But the main question is: How does typeid(..) comparison operator perform? I need to do approx 30 comparisons in the worst scenario per uniform. Of course I would first check for simple regularly used datatypes (ints, floats, etc.) then later for the advanced ones. Of course I did some tests, it seems to perform rather well, but this is on gcc 4.1.0 and on Linux. I am really curious how does it perform on windows and other platforms aswell.

7 Replies

Please log in or register to post a reply.

45d2a3568ad34f6f0297377c4e9dc1b3
0
nachilau 101 Oct 12, 2006 at 17:04

See if this link is helpful

http://www.informit.com/guides/content.asp?g=cplusplus&seqNum=120&rl=1

Base on what I know, the typeid() operator can be very expensive. Most of the compiler actually using string comparsion when checking the type id.

I hope it help!

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Oct 12, 2006 at 17:28

@nachilau

See if this link is helpful

http://www.informit.com/guides/content.asp?g=cplusplus&seqNum=120&rl=1

Base on what I know, the typeid() operator can be very expensive.

For polymorphic types, yes. Because the type has to be investigated at runtime. But for nonpolymorphic types the type is known at compiletime and the type_info object can referenced directly.

code

#include <iostream>
#include <typeinfo>

void compare(const type_info & a, const type_info & ;)
{
    bool equals = a == b;
    std::cout << "[" << a.name() << "] == [" << b.name() << "]: " << equals << std::endl;
}

struct poly_type { virtual void foo() { } };
struct poly_derived_type : public poly_type { };
struct nonpoly_type { };

int main()
{
    int a;
    float b;
    poly_type c;
    poly_derived_type d;
    poly_type & e = d;
    nonpoly_type f;

    compare(typeid(a), typeid(B));
    compare(typeid(c), typeid(d));
    compare(typeid(c), typeid(e));
    compare(typeid(d), typeid(e));
    compare(typeid(f), typeid(a));
}

Assembly:

compare(typeid(a), typeid(B));
004010D1  push        offset float `RTTI Type Descriptor' (433064h) 
004010D6  push        offset int `RTTI Type Descriptor' (433058h) 
004010DB  call        compare (401000h) 
004010E0  add         esp,8 
    compare(typeid(c), typeid(d));
004010E3  push        offset poly_derived_type `RTTI Type Descriptor' (433038h) 
004010E8  push        offset poly_type `RTTI Type Descriptor' (433020h) 
004010ED  call        compare (401000h) 
004010F2  add         esp,8 
    compare(typeid(c), typeid(e));
004010F5  mov         ecx,dword ptr [e] 
004010F8  push        ecx  
004010F9  call        __RTtypeid (408AB9h) 
004010FE  add         esp,4 
00401101  push        eax  
00401102  push        offset poly_type `RTTI Type Descriptor' (433020h) 
00401107  call        compare (401000h) 
0040110C  add         esp,8 
    compare(typeid(d), typeid(e));
0040110F  mov         edx,dword ptr [e] 
00401112  push        edx  
00401113  call        __RTtypeid (408AB9h) 
00401118  add         esp,4 
0040111B  push        eax  
0040111C  push        offset poly_derived_type `RTTI Type Descriptor' (433038h) 
00401121  call        compare (401000h) 
00401126  add         esp,8 
    compare(typeid(f), typeid(a));
00401129  push        offset int `RTTI Type Descriptor' (433058h) 
0040112E  push        offset nonpoly_type `RTTI Type Descriptor' (433004h) 
00401133  call        compare (401000h) 
00401138  add         esp,8

As you can see, for nonpolymorphic types there is already a constructed type_info object ready for use.

As for type_info::operator==():

004089F9  mov         eax,dword ptr [esp+4] 
004089FD  add         ecx,9 
00408A00  push        ecx  
00408A01  add         eax,9 
00408A04  push        eax  
00408A05  call        strcmp (40F0F0h) 
00408A0A  neg         eax  
00408A0C  pop         ecx  
00408A0D  sbb         eax,eax 
00408A0F  pop         ecx  
00408A10  inc         eax  
00408A11  ret         4

Indeed a regular string compare.

VC++ 2005 btw.

9569bfe5ca7c477bc1e6d9587671bb75
0
kulik 101 Oct 12, 2006 at 19:15

Weird thing is, that from my benches, it proved as approximately 2x - 3x slower than regular enum comparison, and really fast for my purposes. I will investigate further if gcc compares the type_infos with string comparisons too.

46407cc1bdfbd2db4f6e8876d74f990a
0
Kenneth_Gorking 101 Oct 12, 2006 at 21:42

Would I be wrong in assuming that the ‘type_info’ returned by two typeid() on the same type would point to the same struct in memory? I don’t know if it is standard, but I just tried modifying .oisyns code to use a modified ‘compare’

void compare(const type_info & a, const type_info & b)
{
    bool equals = (&a) == (&b);
    std::cout << "[" << a.name() << "] == [" << b.name() << "]: " << equals << std::endl;
}

The assembly shows a simple ‘cmp’, and the output is the same as .oisyns unmodified code.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Oct 12, 2006 at 21:50

Yes, that assumption would be wrong. What if the other type_info object came from a dynamic library, for example? The reference would not be the same, but they can still describe the same type.

If the addresses were unique, don’t you tink they would have implemented the operator==() function more efficiently than doing a string compare? ;). You’re code would almost certainly work within the same translation unit, and most probably within the same program as well. But I wouldn’t count on this implementation specific behaviour.

46407cc1bdfbd2db4f6e8876d74f990a
0
Kenneth_Gorking 101 Oct 12, 2006 at 21:54

Good to know :)

9569bfe5ca7c477bc1e6d9587671bb75
0
kulik 101 Oct 13, 2006 at 20:43

For clarification, under gcc, typeid of the type is instantiated once for a boundary if needed. When comparing, not names but rather the memory locations of the typeids is compared. Several people did notice that, but nobody seemed to have taken care of it.

Because strcmp can be a performance eater, I hash the datatype and compare the hashes then, it seems rather fast.