0
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

0
101 Oct 12, 2006 at 17:04

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!

0
101 Oct 12, 2006 at 17:28

@nachilau

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)
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)
compare(typeid(c), typeid(e));
004010F5  mov         ecx,dword ptr [e]
004010F8  push        ecx
004010F9  call        __RTtypeid (408AB9h)
00401101  push        eax
00401102  push        offset poly_type RTTI Type Descriptor' (433020h)
00401107  call        compare (401000h)
compare(typeid(d), typeid(e));
0040110F  mov         edx,dword ptr [e]
00401112  push        edx
00401113  call        __RTtypeid (408AB9h)
0040111B  push        eax
0040111C  push        offset poly_derived_type RTTI Type Descriptor' (433038h)
00401121  call        compare (401000h)
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)


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]
00408A00  push        ecx
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.

0
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.

0
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.

0
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.

0
101 Oct 12, 2006 at 21:54

Good to know :)

0
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.