2
\$\begingroup\$

In my Entity class I've got a function that returns a specific component, that looks like this:

template<typename ComponentType>
ComponentType* GetComponent() {
    for(unsigned int i = 0; i < m_Components.size(); i++) {
        if(ComponentType* cmp = dynamic_cast<ComponentType*>(m_Components[i])) {
            return cmp;
        }
    }
    return NULL;
}

I heard that dynamic_cast is not really elegant and that is also slow. Is there a way to optimaze this code and if so how? Is it really a big deal or does it not matter in a small Super Mario sized 2D platformer?

\$\endgroup\$
2
  • \$\begingroup\$ If you're getting a component by type, you're going to need to check the type. It really won't make an issue to performance at all. If it does, go back and fix it. No big deal. \$\endgroup\$
    – Ben
    Dec 14, 2014 at 10:37
  • \$\begingroup\$ Maybe you can find a way not to try dynamic casting for every component? First check if you got the right component and only cast on return. \$\endgroup\$
    – MB-F
    Dec 14, 2014 at 10:57

2 Answers 2

5
\$\begingroup\$

I heard that dynamic_cast is not really elegant and that is also slow.

"Elegant" is the most worthless word in a programmer's vocabulary. Strike it from your lexicon.

Every programmer has an utterly different idea of what "elegant" means. If you like the code and it works, why worry about if some armchair engineer on the Internet thinks it's "elegant" or not? It's not their code, it's not their project, it's not their concern.

Regarding whether dynamic_cast is slow: it's not the fastest option but it's a perfectly good one. Much bigger and more complex software than your game manages to use dynamic_cast frequently and still work just fine.

Is there a way to optimaze this code and if so how?

There's always a way to optimize code. Do you have a measured and verified performance problem?

Your GetComponent is not optimal, no. There's a few things you can do to make it faster if you want.

You can cache the component type using a type_index which can be constructed with the typeid operator. This won't work as intended if you're querying for base classes/interfaces, though.

If using the type_index then sort the list and do a binary search instead of a linear search. If you stick with dynamic_cast then sorting wouldn't quite work, though you could cache the results and do binary searches on those. boost::flat_map provides a premade data-structure for this or you can roll you own with a vector and std::lower_bound. You could also use a hash table but C++'s unordered_map is not likely to be faster unless your objects have a very large number of components (hundreds or thousands) for some crazy reason.

Querying a "system" in ECS parlance using an O(1) hash table of game object IDs to component indices would maybe be faster than the binary search... maybe. As with all performance work, measure and find out for sure. It'll depend on a lot of factors.

Your real perf gain will just come from not calling GetComponent more than you have to. Doing work faster will never be as fast as not doing it at all.

You can replace the standard RTTI with a custom system (difficult and only slightly faster) or a simple enum (inflexible) or a string name (slower) or several other options. I've been checking lately whether the default RTTI is fast enough for big AAA games and perf-sensitive apps (at least on PC and the latest consoles) and so far the data I have indicates a strong "Yes."

Is it really a big deal or does it not matter in a small Super Mario sized 2D platformer?

Almost certainly not a big deal. If you want to know for sure, measure your performance and find out. The engines I work with will have entirely different bottlenecks than your code, which will have different bottlenecks than any other engine or game.

Measure.

Then make a hypothesis about anything you can improve, update your code, and measure again and make sure you see the changes you expected. Often you'll get a surprising regression, even if you're doing what some game industry veteran on the Internet told you is the obvious fix.

Measure again.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Thank you so much! As a beginner programmer I was wondering why there is such thing as dynamic_cast if I'm not supposed to use it because its not 'elegant'. My character was moving really slowly on the screen and I didn't know if that was because of using dynamic_cast all the time or if I was incrementing its position value by too little.. and also thanks for telling me that dynamic_cast would be fast enough even for some AAA games. So thank you one more time! \$\endgroup\$ Dec 14, 2014 at 13:38
  • \$\begingroup\$ @MarkoKitonjics: haha, be careful with those blanket statements. It's fast enough for some AAA games. Different games have wildly different engines and use cases. For your game, measure and see. If you aren't intimately familiar with a profiler or two then it's time to stop coding and start learning more of the tools in your programmer toolbox. Your character movement question should have been investigated with debugging tools and then the hypothesis about dynamic_cast checked with a profiler. Those tools are about as important as your compiler. :) \$\endgroup\$ Dec 16, 2014 at 0:52
  • \$\begingroup\$ I feel a little embarrassed to say this, but I don't even know what a profiler or debugging tools are :/ ... and yeah I'll stop coding for little bit and I'll start doing some research and learning. \$\endgroup\$ Dec 16, 2014 at 12:32
2
\$\begingroup\$

It depends how often you call this function. If you call it only at start of a game and save pointers you shouldn't care about dynamic_cast. If you call this function in game loop (why?) better avoid it. But if your game is small I think `dynamic_casts' will not decrease performance significantly even if it will be called in game loop few times per frame.

To avoid dynamic_casts you can add static function GetType to each component that returns type of component (for example enum). So check will be:

if ( ComponentType::GetType() == m_Components[ i ].GetType() ) {
    return cmp;
}

But if you want to write engine and allow to add new types of components by other people without changing engine code you can't use enum as GetType return value. In that case you can use 32-bit value. If someone wants to add new component he adds int GetType() method and returns some random number, like this:

static int GetType( ) {
    return 12636354;
}

In this case two different types of components almost always will have different GetType values. Of course almost is not always so you should add some code that checks if some two types of components returns same value. You can make it in some kind of component type registrator class.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks for the idea! And no, I'm not writing such an engine so the first solution is just fine. \$\endgroup\$ Dec 14, 2014 at 13:42
  • \$\begingroup\$ btw I am using dynamic_cast in the main game loop. For example when I press the arrow keys the movement component has to access the player entity's position component and incr/decr it's x value. so: if(key == right) { containingEntity->GetComponent<PositionComponent>()->x++} ... thats a simplified version of whats happening in the game loop, so yes, I am calling it and this is why, any better idea or advice? \$\endgroup\$ Dec 14, 2014 at 14:00
  • \$\begingroup\$ Why you can't save pointer to PositionComponent in constructor or something like this and use it on main game loop? Instance of PositionComponent could be changed during the game? If not, save pointer as class field at start of the game and use it in the game loop. But I'm pretty sure few dynamic_casts in game loop not a performance problem in your case. Dynamic_casts can decrease performance significantly only in large projects when it is used thousands times per second. \$\endgroup\$ Dec 14, 2014 at 14:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .