0
\$\begingroup\$

I am currently working on a game based on SFML and written in C++. For the HUD of the game I have a HUD class with a lot of different variables and methods. Basically most of the variables have a set and get method. We are talking about 14 variables currently and I am sure this will increase.

Example excerpts:

sf::Text m_AmmoText;
sf::Text m_ScoreText;

sf::Text getAmmoText();
sf::Text getScoreText();

void setAmmoText(sf::String text);
void setScore(sf::String text);

Questions

Optimize context is performance vs. code mantainability

1. Prototyp or Inheritance?

I searched the web and found the prototype pattern or the inheritance of classes as the most useful ones to apply. Since I never use both I am quite unsure which solution will have a positive impact on the code and performance. I am aware that with these few classes the performance impact is hardly measurable. It is more for practice and to get it right from the beginning. I started to re-write the code to apply inheritance but thought it might be better to ask the experts first.

Here is an example of the classes. Implementation of methods accordingly to read or write the variable (skipped here).

class HUD{
    protected:
        sf::Text m_Text;
    public: 
        getText();
        setText();
}

class HudAmmo : public Hud {};
class HudScore : public Hud {};

So instead of hud.getAmmoText() I could use hudammo.getText(). I did not code the prototyping yet. But read a lot about its pros and cons on the internet. Suprisingly I could not find a clear direction since most examples I found were abstract. I tend to apply the prototyping pattern from what I understood so far mainly because of this post:

2. Alternative

Re-write the class and have only 1 set and get method which passes the context, e.g. hiscore or other solutions?

\$\endgroup\$
4
\$\begingroup\$

Don't overthink your problems. Keep your designs straightforward and simple until you have proven (by profiling) that you need to complicated them. Then focus on new designs that address the specific problems you found while profiling.

I see nothing in your problem description that suggests you should change your current HUD design at all.


I searched the web and found the prototype pattern or the inheritance of classes as the most useful ones to apply

"Prototype" versus "inheritance" are design choices first and foremost, not performance optimization choices. In general neither will be more or less efficient at runtime than the other. In practice, the specific technique used to implement prototype or inheritance-based object construction may have some minor impact on runtime performance, but generally it will be irrelevant. This brings me to your next concern:

It is more for practice and to get it right from the beginning.

To "get it right from the beginning," you need to correctly understand when it matters to be overly performance-conscious and when it doesn't. That is what is important.

In this case it almost certainly doesn't matter, and no amount of "practicing" techniques you think may be more optimal will actual improve your ability to do so, because you are probably lacking the real-world concerns that actually make something a performance issueand because you definitely lack the knowledge of where those specific concerns occur in this particular case.

When you want to optimize something, the first thing you generally want to do is profile it to make sure you actually understand what is slow. Often this will be enough to let you know that what you think is slow isn't and that your actual performance problem is elsewhere. Then you can go find and focus on that, instead of worrying about micro-optimizations that are dwarfed by a larger issue.

  1. Alternative

What you should do here is probably nothing. Your HUD class, with it's 14 variables, is not a performance concern and it's probably not a particularly serious design concern either. It makes sense, it works, and directly reading and writing variables via accessor methods (that are almost certainly inlined by any modern compiler) will be plenty fast.

Your proposal of creating some kind of HUD base class and then HudAmmo, HudScore, et cetera subclasses that just inherit is worse than what you have now:

  • from a design perspective (you still need to store instances of each of these HudProperty classes somewhere; where?)
  • from a code maintainability perspective (it's a vastly less straightforward design)

And from a performance perspective it's not any different, really; you are still calling inlined accessors to return text variables.

\$\endgroup\$
  • \$\begingroup\$ Thank you so much for this detailed answer and the insights. This will help me a lot! \$\endgroup\$ – Martin Sand Mar 2 '17 at 20:54
  • \$\begingroup\$ "you still need to store instances of each of these HudProperty classes somewhere" -- well you just create a GameHud class which contains one instance of each, and gives access to them via getAmmoHud(), getScoreHud(), etc. Now where'd that chicken go... \$\endgroup\$ – Quentin Mar 3 '17 at 8:47
0
\$\begingroup\$

A few rule of thumbs when performance is a major concern:

Virtual inheritance means chasing a pointer and most likely a branch misspredict.

Chasing a pointer usually means a cache miss.

A cache miss is very expensive (about as expensive as calculating a cosine)

Accessing data that's all tightly packed gets prefetched and is much cheaper.

In otherwords a class containing 14 sf::Texts is cheaper than a class containing 14 pointers to what eventually is a sf::Text.

Note I'm talking about pointers, creating a class that is stored by value and holds several related UI component is fine. For example an ammo display, it holds the icon unique to the weapon, total ammo, currently in the clip and max clip size and a progress bar of the current clip.


Besides that your getters and setters should return and take their variables by const reference.

sf::Text m_AmmoText;
sf::Text m_ScoreText;

const sf::Text& getAmmoText();
const sf::Text& getScoreText();

void setAmmoText(const sf::String& text);
void setScore(const sf::String& text);

Because you want to avoid needing to copy the fields if you don't need to.

Consider defining the simple getters and setter inline inside the class. This will allow the compiler to inline the functions and remove the overhead of the call.

\$\endgroup\$
  • \$\begingroup\$ Thanks for this answer as well! I will take a look into the const reference approach over the weekend. But also your answer confirms that the standard class is the best approach here. \$\endgroup\$ – Martin Sand Mar 3 '17 at 19:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.