Jump to content

  • Log In with Google      Sign In   
  • Create Account

Using global variables - really so bad?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
20 replies to this topic

#1 Shayel   Members   -  Reputation: 256

Like
0Likes
Like

Posted 03 October 2013 - 12:28 PM

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?



Sponsor:

#2 Álvaro   Crossbones+   -  Reputation: 13935

Like
9Likes
Like

Posted 03 October 2013 - 12:42 PM

Do you really need the whole program to be able to play sounds? I would much rather have a "sound rendering" step in the game loop, where I decide what's going to be played and how, and this is the only part of the program that needs to know about the sound system.

 

If you do keep the sound system as a global, you may find that when you try to use something like the game logic of code in another program (for instance, the game logic could be used in a server in a multi-player game, or perhaps we are writing unit tests to make sure an algorithm improvement doesn't change results) you'll discover that you now need a sound system, a pseudo-random number generator, a logging system, a keyboard and a mouse for it to function, although perhaps the game logic shouldn't really need any of these things. Since you made all those things available everywhere, you have no guarantee that people are going to use them only from the places where it's appropriate to use them.


Edited by Álvaro, 03 October 2013 - 12:43 PM.


#3 Melkon   Members   -  Reputation: 533

Like
3Likes
Like

Posted 03 October 2013 - 01:28 PM

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, 03 October 2013 - 01:30 PM.


#4 exOfde   Members   -  Reputation: 810

Like
0Likes
Like

Posted 03 October 2013 - 01:43 PM

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

#5 markr   Crossbones+   -  Reputation: 1653

Like
1Likes
Like

Posted 03 October 2013 - 01:54 PM

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.



#6 Servant of the Lord   Crossbones+   -  Reputation: 21194

Like
12Likes
Like

Posted 03 October 2013 - 01:56 PM

In my limited experience, the larger the program, the more globals it tends to accumulate, and the harder it is to keep track of them, the more likely they'll be used by classes that shouldn't be using them, breaking that class's "self-contained" nature by making them rely on external variables, making the whole program more interdependent than it should be, making it harder to maintain and expand.

 

In small programs, this doesn't seem like a problem, but when I get into the habit of having globals just for the convenience of not doing it the more tedious but proper way, then if that small program ever grows large, or if you come to rely on that convenience and carry it over to larger projects, it might trouble you later.

 

A few key globals in a large project can be useful, but you have to keep your globals in check, because - due to laziness - mine tend to proliferate once I allow myself a few of them, and before I know it, three turns into twenty and then forty and more unless I get and keep them in check.

In a bid to fight them off, but still being lazy, the next poor step I usually accidentally take is by creating a class that pretends to not be global but really is a container for globals in disguise - like a singleton that holds the globals, or a class that holds a huge number of named "properties" (read: globals behind a fake mustache).

Non-private static members of classes are another way of pretending a global isn't a global and deceiving myself.

 

One of the hidden issues with globals is that globals across dynamic libraries can cause compiling problems depending on your compiler, and another problem is that globals (and static members) are initialized in an undefined order. The only guarantee you have is that they are constructed before main() enters, but if one is dependant on another, you don't have any guarantee which will be constructed first.

Though those issues can be worked around, doing so leaves me finding myself investing work into something that makes the code harder to maintain just to avoid writing proper code architecture in the first place (which I have difficulty designing properly, which is why I try to avoid it!).

 

Globals aren't evil, but they are super-easy to abuse.

I like a few constant values, enums, etc... as globals, but anything that could be named a 'system' is something that should be passed into a class's constructor or a function call by pointer. It means more coding work up-front, more fore-thought, more "ugh, why am I bothering to write these extra function arguments" moments, but a cleaner program architecture overall in the long-term.

 

...says the person who rewrites his programs several times because he didn't take the time to think it through and plan it properly.


Edited by Servant of the Lord, 03 October 2013 - 02:03 PM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#7 yckx   Prime Members   -  Reputation: 1291

Like
3Likes
Like

Posted 03 October 2013 - 05:11 PM

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.



#8 tonemgub   Members   -  Reputation: 1164

Like
-2Likes
Like

Posted 03 October 2013 - 05:32 PM

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.

#9 L. Spiro   Crossbones+   -  Reputation: 14425

Like
4Likes
Like

Posted 03 October 2013 - 08:20 PM

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, 03 October 2013 - 08:27 PM.

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#10 Shayel   Members   -  Reputation: 256

Like
0Likes
Like

Posted 04 October 2013 - 01:06 AM

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? ;)



#11 tonemgub   Members   -  Reputation: 1164

Like
0Likes
Like

Posted 04 October 2013 - 01:15 AM

@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, 04 October 2013 - 01:15 AM.


#12 tonemgub   Members   -  Reputation: 1164

Like
0Likes
Like

Posted 04 October 2013 - 01:32 AM

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.



#13 Solid_Spy   Members   -  Reputation: 454

Like
1Likes
Like

Posted 04 October 2013 - 03:11 PM

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, 04 October 2013 - 03:12 PM.


#14 Malabyte   Members   -  Reputation: 589

Like
-1Likes
Like

Posted 04 October 2013 - 11:27 PM

Getters and setters, anyone?


- Awl you're base are belong me! -

- I don't know, I'm just a noob -


#15 L. Spiro   Crossbones+   -  Reputation: 14425

Like
0Likes
Like

Posted 05 October 2013 - 12:40 AM

@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


It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#16 jbadams   Senior Staff   -  Reputation: 19429

Like
1Likes
Like

Posted 05 October 2013 - 03:24 AM

Getters and setters, anyone?

 

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



#17 Wenzil   Members   -  Reputation: 617

Like
1Likes
Like

Posted 05 October 2013 - 11:51 AM

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, 05 October 2013 - 11:58 AM.


#18 yckx   Prime Members   -  Reputation: 1291

Like
0Likes
Like

Posted 05 October 2013 - 11:52 AM

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.



#19 tonemgub   Members   -  Reputation: 1164

Like
1Likes
Like

Posted 16 October 2013 - 10:03 PM

 

@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, 16 October 2013 - 10:04 PM.


#20 Malabyte   Members   -  Reputation: 589

Like
0Likes
Like

Posted 19 October 2013 - 08:41 AM

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, 19 October 2013 - 03:15 PM.

- Awl you're base are belong me! -

- I don't know, I'm just a noob -





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS