By Value vs By Reference In LabVIEW

After my previous post about Learning LabVIEW OOP there were some comments on by reference vs. by value which often come up when talking about OOP. I think there are two reasons that these are tightly linked to conversations about OOP.

  1. In “classical” OOP languages everything is by reference but in LabVIEW OOP is by value. This causes a clash when people have learned OOP from these languages.
  2. We do more by reference work in non-OOP LabVIEW than we sometimes like to admit.

I have been thinking about the techniques and analogs to these lately anyway so this is a bit of a meaty article covering the options that I see for implementing these and some thoughts on how they fit into teaching OOP.

By-Reference vs. By-Value

Lets first define my interpretation of these items. I like to think of them at this level as how data behaves rather than definitions of implementation.

  • By-Value: If you take a wire/data in LabVIEW and change it then it changes only for that piece of code and the code that is dataflow dependent on it. There is also no way another piece of code can change the data on the wire. Branching the wire risks creating a copy.
  • By-Reference: The data of is stored in one memory location. When you make changes they might affect other components that don’t have a dataflow dependency and if you read it twice in a row another piece of code could have changed it.
    (This may differ from a classic computer science definition but my main concern is the behavior I see, perhaps a different term is required but stay with me!)

These strongly overlap with data communication. Another way to think of this is that by-value is communication on a data wire where as by-reference is a tag based communication method (in some situations).

So Why Use By Reference In LabVIEW?

My preference is to always lead with by-value where it works. I think this is key to what makes LabVIEW a powerful language. The data on the wire is yours to do what you want with and you don’t have to worry about side effects when you are programming. I suspect it is one of the keys that makes LabVIEW much easier for people without a software background to pick it up.

There are perfectly valid reasons reasons to use by reference though either in spite of, or because of, these side effects. The following items are cases where I look to by-ref:

  1. Shared Application Resources: This is not a great term but what I mean by this are resources such as an error handler or a system configuration where you want every piece of code singing from the same hymn sheet.
  2. Hardware Resources: This is one of the most common – DAQmx and VISA already all run on a by reference API. If I have to add to the API (attach additional data) I will use a by-ref scheme so it continues to work as you expect.
  3. Huge Data: If a data structure is a big proportion of your application memory you need to make sure you don’t copy it often. You see this in the IMAQ library where images are handled by reference (and the confusion it can cause!). This is the in spite of case – you don’t get any programming benefit but you have to use it due to the constraint of the system.

How To Do By Reference In LabVIEW

There are multiple techniques to get the behaviour I mentioned above. I have put down the key ones below; My favourite will be obvious!

Variables

The simplest and most dangerous technique due to race conditions. This isn’t a problem in single process languages but in LabVIEW it can get you into lots of trouble!

There is one case that I may use them which is for WORM (write once, read many) globals which can be useful for configuration data but I never use them with OOP.

Variables for sharing data

DVRs

Data Value References (DVRs) allow you create your own reference wire to any data type. You access the data through the in-place memory structure which protects the data – no other code can access it until you are finished with it. This is very important for preventing race conditions.

DVRs for by-reference

This is my preferred method for by-ref objects. I would create the class normally but change the standard methods to use DVRs of the class rather than the class itself. What I have found is:

  1. Good – 100% scalable. Want 1/5/20/1000? Not a problem.
  2. Good – The call is synchronous. Once the subVI completes the function it performs is complete.
  3. Good – I have heard criticisms that this can lead to having more wires on the diagram. I think this is a good thing! Wires make it obvious what is coupled to what. Variables and AEs don’t make that obvious.
  4. Good – The property nodes for objects support this with no extra code.
  5. Bad – The boilerplate is tedious! Creating the references and the in place element structures with their weird error handling
  6. Bad – References can be invalid at run time which is avoided with the FGV/AEs.

FGVs/AEs

I’m not going to get into a debate on what term to use for these. What I mean is a non-reentrant VI which uses an uninitialised shift register to store data. Normally developers will have an enum input which defines what function is performed and/or the core FGV/AE is wrapped in another API to allow for an easier connector pane.

These are not traditionally referred to as a by-ref programming method but I would argue they are. The “reference” is which VI you are calling. That is what defines which data is modified or accessed and any changes can be seen anywhere else in the program.

FGV/AE

When I started my company I started down the DVR route already, having experimented with both so I haven’t used this as extensively in big applications but the reason I decided not to start with them were:

  1. No wires so coupling is somewhat hidden (you would have to view the VI hierarchy).
  2. Bloated connector panes (though wrapping it in another API does help with this).
  3. No scalability – you either have to write an addressing scheme into the FGV or maintain multiple versions of the VI.

That said these are the preferred methods of many. It is well understood by different developers and is simpler to create than DVRs and shares the benefits of being synchronous.

It is rare that I use these but one exception is actually when this is the exact behavior I want! If I want a singleton object (for my error handler for example) I create an FGV that stores an DVR/Queue Ref and on the first call it will initialise it. That way I get the same reference everywhere in my application.

Singleton Pattern (based on Aristos Queue Singleton design)

Queued Message Handlers/Actors

This is a bit more of an unusual item to add here but it can be and I believe is used for the same cases as above.

Increasingly these are being used in systems like a module. You have an actor for each instrument you want to talk to for example and you enqueue commands for it to complete.

QMH System (simplified)
QMH System (simplified)

This mode of operation is very similar to a traditional by-ref model. The “reference” is the queue reference or actor reference which ties you back to the “data” stored in the shift registers of the QMH. The QMH loop protects access to the shared resource by processing messages one by one, protecting you from race conditions.

It isn’t exactly like the other options, the key difference is that it can operate independently as well as responding to other requests which can make them hugely more powerful.

There is a major added complexity with this though which is that they are asynchronous. This means two-way communications are difficult for example, what happens if there is an error in the QMH? Also you can’t understand the time from the block diagram. I find I have to create sequence diagrams in order to understand the program flow.

Also you can’t understand the time from the block diagram. I find I have to create sequence diagrams in order to understand the program flow.

This is the reason one of the core tenets of an actor based system is that it is a request and you can’t care when or how it gets done. This rule means you must design your system in order to avoid these complexities, but I think this is a hard rule to follow consistently!

For these reasons, I avoid using these unless I need it to run independently or there is multiple classes interacting within it. I tend to use these for “processes”. For example, a DAQ system where the data just gets published onto an event when it is ready.

So When Do You Teach It?

So coming back to the previous article, when do you teach someone about this?

I would argue that this doesn’t need to be intertwined with OOP. In many cases, if they aren’t new to LabVIEW they will already be using one of these techniques. Sticking with that is the simplest route.

If they are new I believe that the decision between these is probably an architecture decision as each has pros and cons in different scenarios. It is hard to teach them all at once so I would look at your typical architectures. Does one of these tend to form the backbone or default option? (In my case it is the DVRs). In which case I would start with that. You can teach the other methods as they are required. If you suddenly need all four at once you could even hide one method behind another to get a new developer on board quickly.

Let Me Have It!

This is bit of a work in progress in my thought process which the question above prompted. I’m pretty sure my terminology isn’t great but I feel the idea is solid so please comment below or find me on twitter or the NI forums. I find it very helpful to help me understand this better.

10 Comments

  • Steve Watts

    April 24, 2017

    I like the argument that an AE is By Ref by way of a VI. It’s clarified a few points in my head about the fundamental differences in the way I use LabVIEW vs Mainstream.
    Nice work matey!
    (I’ll read this one again in a bit!)

    Reply
  • Steve Swindley

    April 24, 2017

    For some reason I’ve never really got the idea of using DVRs and the whole ‘By Ref’ idea seems to me to go against data flow so is ‘not the LabVIEW way’.
    I’ll quite happily use an AE or two so maybe I’m just using ‘By Ref by the back door’ as it were.
    Thanks for the great post, explains these ideas well.

    Reply
    • James McNally

      April 24, 2017

      This is kind of what triggered my line of thought on this. Why do I seem to be resorting to this more than other people?

      This article comes from me realising that AEs, in particular, are just another paradigm for what can be achieved with DVR and we are all doing the same thing!

      Whilst “by-ref” feels anti-dataflow (and certainly can be!) it is a reality that when we have multi-threaded applications we are going to have to use this to manage shared resources and certain types of shared data.

      Reply
  • Craig Cockburn

    April 25, 2017

    Hi James,
    This is a timely post for myself as I have been consistently struggling to find ways of using by-value whilst experimenting with some OOP projects. I was increasingly under the impression that by-ref was needed and it is good to hear a bit of consencus with this regard. I find myself increasingly using by-value to maintain state of a resource and provide some means of a by-value Interface to the resource . I find this protects the data nicely but also allows easy shared access.

    Reply
  • Krystian

    April 25, 2017

    Good article James. I usually cover both and use whichever is appropriate. I have two libraries one using the other. Making an object a reference is just a tin layer library above the Data object library. I use session framework which automates a lot of wrapping work.

    MyObjSession.lvlibp->MyobjectSession.class—->MyObject.lvlibp->MyObject.class

    That way I have a reference style available for shared application resources and a data flow library if someone needs it.

    I think of the session as a “new” expression in c++.

    Reply
    • James McNally

      April 26, 2017

      Hi Krystian,

      Thanks for your comments. What is the framework you use for this? Or is it something you have created yourself? My main gripe with the DVR approach is the time wasted creating the code and am certainly interested in ways of improving this!

      Reply
  • Nicola

    April 26, 2017

    Thanks for this article (clarify and confirm that is my idea in whats DVR and AEs/FGV is), in my hopinion AEs and DVR is not comparable because have two differents scopes. We use DVR to build the api, but in final application we use a FGV/AEs to modularize the DVR code and give all more readable. Thaths i’d like to say is AEs can be a container for DVR or other by reference data.

    Reply
  • Sam

    April 27, 2017

    I don’t use OOP extensively in most projects, but I do find it useful for data sharing and hardware libraries and where it makes sense to take advantage of dynamic dispatch.
    I tend to do the ‘inverse’ of the DVR method you have proposed. Rather than creating a DVR of the object, I use a DVR of a type ded for the private data of the class and in my constructor for the class, I create the DVR and store that as the private data (so my private data just contains a DVR of the typedef). All the methods in my class then use the in-place element to unbundle/re-bundle from the DVR. If that’s clear?
    I don’t really see that method being used very much (I guess because LV has some special options for creating DVRs of classes) but it saves on creating lots of boilerplate VIs and hides away the implementation of the by-ref (making simpler APIs?).
    I’d be interested to hear others thoughts on the matter though!

    Reply
    • James McNally

      April 27, 2017

      Hi Sam,

      I’m pleased someone brought that up!

      I have done that in the past, I have also used by value objects that i know are just wrapping a queue which give them by reference behaviour.

      I have settled on the approach I show for a couple of reasons. One is you see the wire is a reference so as a customer of the API there is no doubt it is by reference and you can be aware of it.

      That said i have used it where i want the opposite. From the point of the caller it doesn’t care if it has to be implemented by reference, perhaps a design constraint as forced it to be by ref at this time so I have used that method too there.

      I’m talking myself out of this in different ways now! I think both approaches have merit

      Reply

Leave a Reply


By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close