• Advertisement
Sign in to follow this  

Using global variables - really so bad?

This topic is 1553 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi
Ill give you an example.

g_SoundSystem;
class Game
{
  sf::RenderWindow screen;
  State* CurrentState;
public:
  void Start()
  {
     CurrentState->Logic();
  }
};

class Level1
{
  Player player;
public:
  void Logic()
  {
    if(something) g_SoungSystem->Play("shot.wav");
  }
}

CurrentState=new Level1();


I heard that global variables are bad.. but what is the other way to use things like "Soundsystems" etc. g_SoundSystem has to be global so i can use it from any place in the program. I was also thinking about setting flags in certain points but that would also require global access. In other words: Can you help me in understanding how to replace globals?

Share this post


Link to post
Share on other sites
Advertisement

Don't use global variables, if you really want to use a variable alot, you can create it in your main and pass through to functions that need it.

 

Well, in a small project some global variable won't cause any problem, but you can avoid it and in a bigger project it's pretty unreadable.

Better forget global variables as early as possible and always searching the way to use every variable in the smallest scope that possible.

Edited by Melkon

Share this post


Link to post
Share on other sites
I learned in the past one simple answer to your question: it's much more readable!

of course it would be no problem to use global variables for any kind of problems but the simple thing is how bigger your program grow how bigger grow the amount of time you need to repair/add or change something in a proper way!

but if you cannot read your program properly e.g. After 2 years you will run crazy! The same is with singletons, in my eyes the better global variable but unfortunately still the same. In the end it is your decision, perhaps you have some serious reason to use them, then use it. You are free in your decision^^.

Share this post


Link to post
Share on other sites

Really having a few globals hanging around is not so bad, especially if they are in their own namespace and there aren't *too many*.

 

So if you have a few "big objects" which you want global instances lying around, then use globals, it's not really a problem.

 

The problem really starts when:

 

* The maintenance programmer can't tell which variables are globals / members / locals etc, easily

* There is so much unstructured global state that it becomes easy to get "stale data" bugs.

Share this post


Link to post
Share on other sites

An important question here is: does the global have a state that can be changed anywhere in the program? I suspect that a sound system would have state. Such globals are much more difficult to debug, because the problem can be anywhere in the code base, or even worse, the result of multiple lines of code that are widely separated.

 

It's much better to pass the object around where needed, and better still to have a sound rendering step as Alvaro mentioned, to restrict the scope of the object as much as possible.

Share this post


Link to post
Share on other sites
Theoretically, (non-pointer) globals should be faster, because their fixed-addresses are established at compile-time. In contrast, local members or variables that are not declared static will usually be dynamically allocated on the stack or heap, so their memory addresses have to be calculated at run-time, when they are accesse. In the worst case, that calculation is compiled as a simple pointer+offset addition. Practically, the performance of a memory access is usually ruled by other factors than how the address is calculated, and a lot more performance can always be gained with higher-level optimizations than using globals. In fact, locals have an advantage when it comes to being cached by the CPU - since they are always "close" to the other local variables from their parent (stack or object), they have a lower chance of being discarded from the CPU data cache. If your program were to use only global variables, with no dynamic allocation at all, only then you would be able to say that the globals are faster, but nobody does that anymore. :) Plus, initialized globals will increase the size of your executable.

Share this post


Link to post
Share on other sites

Theoretically, (non-pointer) globals should be faster, because their fixed-addresses are established at compile-time. In contrast, local members or variables that are not declared static will usually be dynamically allocated on the stack or heap, so their memory addresses have to be calculated at run-time, when they are accesse.

On x86/x64, the MOV instruction takes the same number of cycles regardless of the operand types, cache stalls non-withstanding.


L. Spiro Edited by L. Spiro

Share this post


Link to post
Share on other sites

Okay, I get why global variables can be bad, but I still don't know how to replace them. In my example it was easy to set correct sound to be played. Now let's say I don;t want to use the global SoundSystem. I could make SoundSystem member of a Game class. But how do I set the correct sounds now, when the "Player" class inside the State, which is already inside the game class, doesn't have access to the SoundSystem. It was easy to check.. if(playerState==state_attacking) g_SoundSystem->PlaySound("swordfling.wav");

 

Another idea was to pass the m_SoundSystem(member of the game class), to the State->Logic, and then to the Player.Logic() but that makes no sense to me. Well, at least I have never seen a source code/example with such a solution.

 

Or maybe I don't need anything like SoundSystem? Maybe simple function playing sounds is enough? However I find SoundSystem much more useful, as it can track which sounds are playing so I don't get the "100000 sounds at a time" effect etc.

 

Any tips from more advanced coders? ;)

Share this post


Link to post
Share on other sites

@L. Spiro: I think you are right, but I didn't say that immediate values are faster because they use fewer clock cycles.tongue.png

Edited by tonemgub

Share this post


Link to post
Share on other sites

Okay, I get why global variables can be bad, but I still don't know how to replace them. In my example it was easy to set correct sound to be played. Now let's say I don;t want to use the global SoundSystem. I could make SoundSystem member of a Game class. But how do I set the correct sounds now, when the "Player" class inside the State, which is already inside the game class, doesn't have access to the SoundSystem. It was easy to check.. if(playerState==state_attacking) g_SoundSystem->PlaySound("swordfling.wav");

 

Another idea was to pass the m_SoundSystem(member of the game class), to the State->Logic, and then to the Player.Logic() but that makes no sense to me. Well, at least I have never seen a source code/example with such a solution.

 

Or maybe I don't need anything like SoundSystem? Maybe simple function playing sounds is enough? However I find SoundSystem much more useful, as it can track which sounds are playing so I don't get the "100000 sounds at a time" effect etc.

 

Any tips from more advanced coders? ;)

If your only issue is handling multiple sounds at a time, you don't need a global variable keep track of that. It sounds like you're already doing something to have the sounds played in order by waiting for the active sound to finish before starting a new one, or to stop all the active sounds from playing when you want to start playing a new one. You can just do this directly in your Player class.

In the  future, you might want to have some of the sounds playing at once, and some not, depending on the state of the Player. The best place to decide how to play the sounds is inside the classes that represent your game objects. On the other hand, if you code a separate class just for the sounds, you'll have to pass in the objects' states into this class, and then do the same decisions of how to play the sounds a second time inside the PlaySound method.

Share this post


Link to post
Share on other sites

Okay, I get why global variables can be bad, but I still don't know how to replace them. In my example it was easy to set correct sound to be played. Now let's say I don;t want to use the global SoundSystem. I could make SoundSystem member of a Game class. But how do I set the correct sounds now, when the "Player" class inside the State, which is already inside the game class, doesn't have access to the SoundSystem. It was easy to check.. if(playerState==state_attacking) g_SoundSystem->PlaySound("swordfling.wav");

 

Another idea was to pass the m_SoundSystem(member of the game class), to the State->Logic, and then to the Player.Logic() but that makes no sense to me. Well, at least I have never seen a source code/example with such a solution.

 

Or maybe I don't need anything like SoundSystem? Maybe simple function playing sounds is enough? However I find SoundSystem much more useful, as it can track which sounds are playing so I don't get the "100000 sounds at a time" effect etc.

 

Any tips from more advanced coders? ;)

What I would recommend is making a base class for your entire game, and adding a SoundEngine object INSIDE the base class. If you ever want to use the sound engine inside an object, you pass it in as a parameter. I would look at the Rastertek tutorials for examples.

Edited by Solid_Spy

Share this post


Link to post
Share on other sites

@L. Spiro: I think you are right, but I didn't say that immediate values are faster because they use fewer clock cycles.tongue.png

Outside factors notwithstanding, cycles are what determines speed.

 

As for outside factors such as cache misses etc., globals are more likely to have cache misses than locals (stack data) and equally likely to have misses compared to heap allocations.  The stack is accessed so frequently it is almost always cached, whereas globals and heap data are spread apart and accessed somewhat randomly, or at least less frequently than local areas on the stack.

 

In other words, locals tend to perform the best.  Some devices such as Nintendo DS even reserve their fastest RAM for stack space due to how needed the performance of the stack is.

 

 

L. Spiro

Share this post


Link to post
Share on other sites
Inject the sound system in Level1's constructor.
 
public int main()
{
  SoundSystem* soundSystem = new SoundSystem();
  Level1* level1 = new Level1(soundSystem);
}

class Level1
{
private:
  Player player;
  SoundSystem* soundSystem;
public:
  Level1(SoundSystem* soundSystem) : soundSystem(soundSystem)
  {
  }
  void Logic()
  {
     if(something) soundSystem->Play("shot.wav");
  }
};

Edited by Wenzil

Share this post


Link to post
Share on other sites

How do you handle your graphics? It may help to think about sound as another rendering system, using audio instead of video as a medium. How does your renderer know what to draw, and where? Do you feed it renderable objects? Does it poll a scene graph? A similar approach shouldn't be difficult to design for your audio system.

Share this post


Link to post
Share on other sites

 

@L. Spiro: I think you are right, but I didn't say that immediate values are faster because they use fewer clock cycles.tongue.png

Outside factors notwithstanding, cycles are what determines speed.

 

As for outside factors such as cache misses etc., globals are more likely to have cache misses than locals (stack data) and equally likely to have misses compared to heap allocations.  The stack is accessed so frequently it is almost always cached, whereas globals and heap data are spread apart and accessed somewhat randomly, or at least less frequently than local areas on the stack.

 

In other words, locals tend to perform the best.  Some devices such as Nintendo DS even reserve their fastest RAM for stack space due to how needed the performance of the stack is.

 

 

L. Spiro

 

I thought that was exactly what I said (except for the DS bit), but nevermind... smile.png

Edited by tonemgub

Share this post


Link to post
Share on other sites

Not relevant to the topic at hand... huh.png

I might've misunderstood the effective level of privacy that getters and setters can provide. Nvm I guess, just a thought I had after reading Servant's reply. unsure.png

Edited by Malabyte

Share this post


Link to post
Share on other sites
It is almost impossible to unit test code that relies on mutable global state, and where it is possible it is usually annoying.

Another issue is that a global makes it very easy to create what would otherwise be considered insane dependencies between distinct subsystems.

Of course, what is the argument for globals, and is it compelling? The most common is it is inconvenient to pass references to these objects everywhere. Of course, this raises two immediate counter arguments. The first is that if the would-be global is really used nearly everywhere, then your project is spaghetti, everything is tightly coupled and there appears to be no modularity. The second if the global isn't used everywhere, then passing to the areas that actually need them probably isn't quite as inconvenient as first thought.

For simple games that beginners make, there are a few approaches that can help. One common issue is that the logic to check for input might end up buried into a Player class, for example. One solution might be to move the logic to check for input away from the player, and to call member functions on the Player class when input is received/

Another is to bundle a few critical systems that really are needed in lots of places (in one game I made, objects to represent the logical and physical game world, the current level, a simple configuration mechanism and the resource caches) together in a context object. This avoids the need to pass several parameters through to the objects that need them. Care needs to be taken with this approach, because if every area of the game has access to the critical objects then nothing has been gained.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement