Tuesday, June 8, 2010

I Like Global Variables

I think the title of this post alone will probably lose me some followers on this blog. :) Nevertheless, let's proceed...

Here's the deal...global variables are not evil. There are perfectly valid use cases in which a global variable is the best tool for the job, and doing something more complicated just to say, "I didn't use globals!" doesn't make sense. Now sure, globals can be abused, and there are certainly scenarios where they are contraindicated. But if you're aware of those situations, and you avoid them, you should feel free to use globals where needed. So let's look at some use cases where, in my opinion, globals can come in handy:
  • Static Data - Whenever I create a UI in LabVIEW in which user-visible strings are programmatically changed, I always store those user-visible strings in a global variable. Let's call this kind of global a Write Never, Read Many (WNRM) global. I am never writing new values into this global...I'm only reading from it in my code whenever I need to update a user-visible string. Another use case for WNRM globals is in scripting apps. When I'm doing code generation involving templates, I always store the identifying labels of panel/diagram objects within a global, and I always read those identifiers from a global in my scripting code. That way, if I need to change the identifiers in the templates, I know that I'll only need to change the identifier in one place (my global) in my scripting code. (Note: I believe Yair's Adding CONSTs to LabVIEW idea on the Idea Exchange is intended to facilitate a cleaner implementation of this use case.)
  • Debug Flags - If I've got parts of my code that I want to run differently when I'm debugging, I use a WNRM global. I'll open the global and change the debug flags before I run my code. Someone once suggested that I should instead use Conditional Disable Structures with conditional symbols in my project, but after I looked into it, I decided that the added complexity and configuration associated with conditional symbols didn't add enough value over the simple global solution.
  • Configuration Data - In this scenario, we are initializing configuration data (from an INI file, for instance) at some point in our code, and reading that data at later points. So here we have a Write Once, Read Many (WORM) global. (Note: The term WORM global was first coined by tbob on the NI Forums a few months back.) As long as there is one, single place where the global is written, it is perfectly fine to have many other places in your code that read that global.
Now one of the biggest reasons given as to why globals should be avoided is that new users could easily find themselves in race conditions if they're not careful. But with the use cases above, there should never be any worry about race conditions, as long as developers adhere to the WNRM/WORM constraints. I don't think we should hamstring ourselves with a blanket refusal to use a feature when it really can be a performant time-saver when utilized correctly.

14 comments:

  1. I love the train-wreck-watcher drawing on the top.

    As you mentioned, I agree with you in principle, as evidenced by the fact that both threads you linked to are mine.

    The main advantages globals have are that they are super easy to create and use (you get a closed list with strict types) and they break the code when you modify them.

    However, I do agree that the dangers of globals are sometimes unacceptable and that we could have better tools.

    As I mentioned in the other thread, one example would be a read only global VI with a diagram. The diagram would run once when the VI loads. The problem is that this has all kinds of issues.

    Here's another thread where the issue was discussed: http://lavag.org/topic/11230-is-it-acceptable-to-use-global-vis-in-a-class-if-they-are-scoped-privatecommunity

    P.S. The thing I love most is that people leave globals behind to use get-set LV2 globals instead, which have no advantages at all over globals (and I believe I've been guilty of this as well on occasion).

    ReplyDelete
  2. While you are correct that globals have their uses, there are other ways to implement a WORM without globals. My favorite is a simple VI with one input and one output. The control and indicator are strict references to the desired data type. the block diagram is a feedback node with the input as the initializer and the output connected to the output of the node. The node feeds back to itself. This allows you to only write to the input once while your code is running but read from it as many times as you want. Also, once your code stops, the data is no longer available.
    Here is a good discussion of this WORM method:
    http://lavag.org/topic/7174-a-labview-worm/

    With regards to Yair's comment about LV2 globals not having any advantage over normal globals, I would disagree. The LV2 global protects the data against parallel reads while an update is being done. Real globals do not protect the data this way.

    -John

    ReplyDelete
  3. The problem with the feedback node WORM is that it's still not as easy to create as a global is.

    As for the LV2 globs comments, I was specifically refering to the get-set variety (which I've seen many times). This functions exactly like a global.

    ReplyDelete
  4. I agree with Yair...I don't see any reason to go to all the trouble to create a WORM VI for each piece of data I want WORM access to, when I trust myself to just use a global variable properly.

    And yes, I have also written the get/set variety of functional global before...totally a waste of time when a simple global would have sufficed.

    ReplyDelete
  5. Darren, I do think that things which protect the programmer are better than things which don't.

    I don't usually have the need for a real WORM, but I wouldn't mind if I had better control of who has access to write to globals.

    Alternatively, I could also live with tools which would make it easier to create the relevant LVOOP code. I use globals because they work and have certain advantages, but I recognize that they have some real problems, which I wouldn't mind seeing resolved.

    ReplyDelete
  6. Great post Darren. I totally agree - there are so many things in every programing language that are shunned as bad practise by the pundits that just aren't - they can be used innappropriately and *that's* bad practise, but that doesn't make the technology bad. I've seen experienced programmers go to extraordinary lengths to avoid globals, locals, sequence structurs, datasocket nodes, etc, and end up with less-maintainable and intuative code, not to mention the time wasted.

    Bottom line: there are no bad technologies in LabVIEW, only innappropriate ways to implement them :) Hopefully your post and the eight of the LabVIEW Champioms that are commenting on it will bring a seachange in the community. Kudos!

    cheers,
    crelf

    ReplyDelete
  7. "...the eight of the LabVIEW Champioms that are commenting on it..."


    I know I gained some weight, but I didn't realize it was THAT much. ;)

    ReplyDelete
  8. Hi Darren - great post.

    Your last sentence on you blog post sums up how I feel about this topic. I was told from the get-go that globals are evil so I never used them. But nowdays I don't see the point in writing all that additional code for a FGV when I can use a global safely under the conditions you outlined. So I have started using them more.

    A few questions:
    For a project, do you personally find it better management to have individual global VIs (files) - or one global VI containing all variables or a combination of both?

    Also do you like to scope your globals (as per Yair link to AQ's post)?

    Cheers

    -JG

    ReplyDelete
  9. JG,

    I like to have a single global for all my variables, but I organize them on its panel into nice sections with decoration borders, etc.

    I have never looked into scoping my globals. Haven't yet seen the need.

    -D

    ReplyDelete
  10. I normally put all the database table names into a single global VI. So that in case of table structure changed, I would know which SubVIs I need to re-validate.

    :) landlord From LAVA

    ReplyDelete
  11. Great post Darren.

    ReplyDelete
  12. Hi Darren,
    I am probably very late in replying to your post, but I have question regarding the performance of Global vs Functional globals.
    I have a configuration file with 60 variables which currently I am storing in gloabal variables. The variables are written at initialization, during program execution(I am sure there are no race conditions) and in the end write the values into the ini file.

    I want to know whether accessing the values are faster using globals or functional globals?

    Thanks,
    Ritesh

    ReplyDelete
    Replies
    1. I have never performed formal benchmarks, but I would guess that regular globals would beat functional globals in just about all cases (reading or writing). As I mentioned in the post (and as has been mentioned in this comment thread), there's really no reason to use a functional global unless you're going to give it more varied operations than just 'write' and 'read'.

      Delete
  13. Using an existing global from on project in a new/another project adds lots of dependencies, many not really necessary for the particular global, this makes loading the new project slow and the project bulky. This doesn't happen with FGs.

    ReplyDelete