‎08-01-2018 10:42 PM
Scenario: my main VI contains two tightly timed loops which must stay in lockstep with the hardware. These are constantly feeding data to the device using Ethernet/IP protocol. The data changes according to what I want the device to do, and I use local variables to send the data changes into the tight loops.
The processes I would like to put the device through are sometimes complex, and I would like to make subVIs of them. Preload, Calibrate, Run process, etc. But then they can't feed data to the hardware control loops using local variables! I would have to use global variables instead, and I have read that global variables are a performance hit. Am I right about that? What is the most efficient way to feed data from an executing subVI called from one loop, into the ongoing process of another loop?
‎08-01-2018 11:15 PM - edited ‎08-01-2018 11:18 PM
Using locals is almost always a bad idea for anything but the quickest code hacks, especially in "time-critical" scenarios (which you probably won't be able to achieve reliably if you are running, say, Windows). Each local variable node call requires the UI thread, and there's only one of those - so your calls will only execute one at a time, as well as sharing time with the UI and other "root loop" house-keeping.
You can use Global variables as you suggest; they are just a VI call so they have very little overhead. That probably answers your main question. But you should be careful how you use them; Globals going 'one-way' (eg. Write only, Read only) are a perfectly ok design decision but require a certain amount of diligence. To feed data between loops the best options are typically synchronisation primitives such as Queues, Notifiers. If you are using LV 2016+ Channel wires are another good option (which are just wrappers around the base primitives from an execution perspective). These types of nodes can be configured for a number of scenarios, including limiting your overhead in tight-loops.
In the end the best design decision really depends on your platform, timing, re-usability and maintainability requirements. You'll get better advice if you provide more specific information.
‎08-02-2018 06:04 AM
@Ken_Brooks wrote:
I would have to use global variables instead, and I have read that global variables are a performance hit. Am I right about that?
No.
You wouldn't have to (and shouldn't) use global variables.
Reading global variables won't be a performance hit either. Each instance of a global or local is a copy in memory of the data. For scalar values, that's probably not going to be noticeable in performance.
@Ken_Brooks wrote:
What is the most efficient way to feed data from an executing subVI called from one loop, into the ongoing process of another loop?
You should ask yourself if "the most efficient" is needed. I'd go for "efficient enough".
Note that locals are probably on a visible front panel, while globals are usually on a closed front panel. Globals probably perform better then most alternatives (even locals!), at least performance-wise. Definitely not when it comes down to maintainability and readability.
Functional Globals (FGV), DVR's, Queue's, by reference Classes are all options.
‎08-02-2018 06:54 AM
@tyk007 wrote:
Each local variable node call requires the UI thread, and there's only one of those - so your calls will only execute one at a time, as well as sharing time with the UI and other "root loop" house-keeping.
Completely WRONG. A local variable does not have anything to do with the UI thread. Writing to a local variable acts the same as writing to a terminal, just with more memory usage. It will put the data into a buffer and when the UI thread is allowed to run again, it reads the data from said buffer to update the graphics. I think what you were trying to refer to are the Value Property Nodes. Property Nodes that are referenced to anything on a front panel have to run the the UI thread and force redraws.
‎08-02-2018 07:02 AM
@Ken_Brooks wrote:
I would have to use global variables instead, and I have read that global variables are a performance hit. Am I right about that?
You are not right about that, at least in modern versions of LabVIEW. Global Variables did get a major performance boost somewhere along the way.
@Ken_Brooks wrote:
What is the most efficient way to feed data from an executing subVI called from one loop, into the ongoing process of another loop?
Well, now we have to get into more specifics. What kind of data are you trying to pass along? Tag data (only care about the latest value, ignore any missed values) or Streamed data (have to process every value, high throughput)? I will assume we are not talking about Message data (intermittent, probably care about every value).
So, if we assume Tag data, then Global Variables are great. As with almost all Tag data, make sure there is only 1 place where the values are written.
And here are a couple of articles/presentations you should really look at:
Are Global Variables Truly Evil?
‎08-02-2018 07:05 AM
@crossrulz wrote:
@tyk007 wrote:
Each local variable node call requires the UI thread, and there's only one of those - so your calls will only execute one at a time, as well as sharing time with the UI and other "root loop" house-keeping.
Completely WRONG. A local variable does not have anything to do with the UI thread. Writing to a local variable acts the same as writing to a terminal, just with more memory usage. It will put the data into a buffer and when the UI thread is allowed to run again, it reads the data from said buffer to update the graphics. I think what you were trying to refer to are the Value Property Nodes. Property Nodes that are referenced to anything on a front panel have to run the the UI thread and force redraws.
Agreed.
Locals probably have a little overhead (over closed globals), because the values will get updated at some point (e.g. when updating the UI, the copies of the data need to be compared, and the last update needs to be visualized). This update will happen in the UI thread, but it will not stall the thread that sets the local (, as the UI thread has the copy to work on). This overhead will be tiny for scalars. I would no worry about that at all.
A memory copy for each local is the price for not being linked to the UI thread. Indeed, value property doesn't require a data copy, but the execution thread will synchronize with the UI thread as a consequence.
‎08-02-2018 08:06 AM
@crossrulz...ongoing process of another loop?
Well, now we have to get into more specifics. What kind of data are you trying to pass along? Tag data (only care about the latest value, ignore any missed values) or Streamed data (have to process every value, high throughput)? I will assume we are not talking about Message data (intermittent, probably care about every value).
...
Yes, to be able to properly suggest a solution we need to know exactly what the problem is that you are facing.
I agree with much of what has been offered but will offer another approach that let control the throttle level setting of a jet engine simulator running in LV RT 6.1.
At that time is was suggested that Action Engines be used to transfer info into a time critical loop. Action Engines were mentioned above under the title of Functional Globals. The detail I want to highlight with this contribution to the thread is the "Skip if Busy" option if an Action Engine is set as "Subroutine" priority. That option can be set for each instance of the AE.
That setting allows for a violation of the data flow paradigm in that if the AE is executing when it comes time to invoke that call of the AE the call will be skipped and the calling loop/function will not be stalled. When the call is skipped, the default values returned by the AE are used instead of the actual return data. In my case I returned a Boolean that was set true by the AE. When the Boolean was found to be "True" by the caller, I would use the values returned by the AE. If the Boolean was false I would ignore the default data and use the data I had cached from a previous call of the AE.
Summarizing...
An Action Engine configured as subroutine can leverage the Skip If Busy" option to be able to pass data into a time critical loop without stalling the Time Critical loop.
So add that option to the list of options and choose what best fits your needs.
Ben
‎08-02-2018 08:32 AM
Note: the OP is using TCP/IP, and is thus nowhere near "tightly timed" enough for the relative overhead of the various techniques to matter.
‎08-02-2018 08:37 AM
@drjdpowell wrote:
Note: the OP is using TCP/IP, and is thus nowhere near "tightly timed" enough for the relative overhead of the various techniques to matter.
Here is a link to an old thread where I was questioning why one read of a global would be slower than other reads of the same global.
In that situation I was using a Global as a "stop" flag that was checked by about 250 timed loops that were synced to hardware clock in LV 7.1.
When I used the "Global" some of the loops would return "Finished Late". Replacing the Global with an AE fixed the problem.
There was very definitive fast nailed down in that thread... if you are interested.
Ben
‎08-02-2018 08:40 AM
@drjdpowell wrote:
Note: the OP is using TCP/IP, and is thus nowhere near "tightly timed" enough for the relative overhead of the various techniques to matter.
Not all of EthernetIP is TCP/IP.
Datagrams can be very fast.
Ben