Jump to content

Constant Folding and State Machine inputs


Recommended Posts

I realize that the answer to this question is probably more complicated than just a yes/no, and it may to lead to more questions, but I'll ask anyway.

I do a lot of simple state-machine architectures for data processing routines that need to retain information from one iteration to the next. I typically will use an Init state, which initializes uninitialized shift registers (USRs), then a run state to pull data from the USR, and do the number crunching.

What is the best way to get data in and out? I'm showing 3 options below.

post-4344-124788417175_thumb.png

I know that the third option is a No-No. That's been discussed here before.

What about the first two? Is the presence of the constant folding wires in the 1st option LabVIEW's way of telling me that the Control terminal should go outside the while loop?

What about on the output side? Does it matter if the Indicator terminal is inside the loop vs. outside?

I'm just full of questions this week.

Thanks,

Gary

Link to comment

As a general rule I always put subvi controls and indicators on the top-level diagram outside any structures. I believe that usually results in the most efficient compiled code.

In normal looping situations with n loops option 1 will typically outperform option 2. In option 2 every loop creates two additional copies of Data In for a total of 3 copies; one for the front panel control, one for the wire, and one for the front panel indicator. (Note: It will do this if the front panel is open. It may not if the front panel is not open... I don't recall for sure.) However, since this loop always only executes once logically it shouldn't make any difference in terms of memory allocation and data copying. Another thing to look at is using for loops instead of while loops. I read somewhere that a for loop with a constant 1 turns out more efficient code than a while loop with a constant TRUE. IIRC the compiler doesn't recognize they are functionally equivalent and still executes a check at the end of the while loop to see if it should continue.

Link to comment

post-4344-124788417175_thumb.png

I always go with option 1. However as this is a typical FGV (Conditional Stop always TRUE) and not a MFVI/module (Conditional Stop determined by logic) as Daklu mentioned if it only iterates once then it probably does not matter.

But with a MFVI - I saw some unexpected behaviour on RTOS with arrays that was seemed to be fixed going from Option 2 to Option 1 model.

I guessed that on every iteration of the loop, the array is being re- read and possibly a new allocation is made???

I too am interested to know what is happening at the memory management level on multiple iterations.

FWIW: I also put the command enum outside the loop as well. In fact even for SubVIs I put all controls on the left inline, and all indicators on the right inline for neatness/readability as well.

Edited by jgcode
Link to comment

None of those options are really better than any other - depending on what you're trying to do. The first option is the best for performance (memory and speed), but the FP nodes will only be read from/written to once. The middle one has slightly worse performance, but if it's important for the user to interact with the FP between loops, then it's a good solution. The third one is fine too, although it will wait to get the data from the FP until it enters the "run" case - giving your user a chance to change the FP control before it goes in there - it also updates the indicator as soon as the subVI has run. In summary, the answer is "it depends" (I have a sense of de ja vu). If this is a headless subVI then you want to go for option 1. If you need operator interaction (them twiddling the control) then you need to look at either 2 or 3.

Note: I'm assuming that there are no locals or property nodes that access the value property of the FP nodes in other cases.

On a second look: the diagrams you show don't really look like state machine architectures - they're more like functional globals. In that case, I'd suggest you have the FP control in the state that uses it, and the FP indicator in that state that uses that. There doesn't look to be any reason to have them anywhere else, because no other states (?) use them - they all rely on the data in the USR. Maybe I'mnot interpreting the diagrams correctly...

I also put the command enum outside the loop as well. In fact even for SubVIs I put all controls on the left inline, and all indicators on the right inline for neatness/readability as well.

It is, indeed, better for readability of the diagram, but it can lead to unexpected behaviour. Especially if you think about outputs: if you have an output outside of loops/structures/etc then the value passed to it may be different to that if you had it inside the only structure that really needed to access it. For example, if you create a subVI with the usually-appropriate error case around it, there's no reason (other than asthetics) to have all of your inputs outside of the case - they should be inside the "No Error" case (except for the error clusters of course biggrin.gif ). If you have other control outside, you're tempted to have all your indicators outside to, and then you have to handle what values go to them, even when the case that usually handles their value is never executed. That said, sometimes it makes sense to do that: like when you need control of values. In summary, the answer is "it depends" (I have a sense of de ja vu).

Link to comment

...Especially if you think about outputs: if you have an output outside of loops/structures/etc then the value passed to it may be different to that if you had it inside the only structure that really needed to access it. For example, if you create a subVI with the usually-appropriate error case around it, there's no reason (other than asthetics) to have all of your inputs outside of the case - they should be inside the "No Error" case (except for the error clusters of course biggrin.gif ). If you have other control outside, you're tempted to have all your indicators outside to, and then you have to handle what values go to them, even when the case that usually handles their value is never executed. That said, sometimes it makes sense to do that: like when you need control of values. In summary, the answer is "it depends" (I have a sense of de ja vu).

When the VI executes as a subVI, LabVIEW has to put some value on the output indicators. regardless if the indicator is inside or outside the loop, the default values are used anyway right? So I think you have more control of the values if they are outside the loop, then you can place constants to define your outputs in the unused cases.

Link to comment

For example, if you create a subVI with the usually-appropriate error case around it, there's no reason (other than asthetics) to have all of your inputs outside of the case - they should be inside the "No Error" case (except for the error clusters of course biggrin.gif ).

Are you sure about that? I'm sure I've read that putting input controls inside a case statement prevents LV from making certain compiler optimizations. I can't find the reference at the moment though.

If you have other control outside, you're tempted to have all your indicators outside to, and then you have to handle what values go to them, even when the case that usually handles their value is never executed.

Meh... 'Use Default if Unwired' smile.gif

Link to comment

Are you sure about that? I'm sure I've read that putting input controls inside a case statement prevents LV from making certain compiler optimizations. I can't find the reference at the moment though.

Here is a great piece of knowledge base from Adam Kemp. Twas posted on Info LabVIEW earlier this year.

It maybe the reference you are thinking of? The PowerPoint is a good read.

Subject: RE: Overhead of Sub-Vi-calls - was: Code condensing?

From: "Adam Kemp" <Adam.Kemp@ni.com>

Date: Wed, 6 May 2009 11:08:12 -0500

One way to make subVIs very expensive is to write them in such a way that

their inputs or outputs get copied unnecessarily. This is especially bad

if your inputs are arrays. Here are some tips to avoid copies and

otherwise improve performance of subVIs:

1. Make sure all indicators are on the outermost level of the diagram (not

in case structures or other structures). Putting them inside another

structure requires extra code to do things like set them to their default

value if that case is never executed.

2. Put controls and indicators on the connector pane. If it's not an input

and it's not a constant then we have to go to the UI to get/set the value,

which may mean a thread switch and always leads to at least one copy.

3. Mark inputs as required. This eliminates code to check whether they're

wired and use default data if they're not. Unwired inputs have to make a

copy on every run because they can never overwrite the default value. It

also avoids bugs when you forget to wire them (this has saved me many

times, and cost me lots of time when I forgot to do it).

4. Use shift registers for loop tunnels (especially For Loops, which

require special code on output tunnels in case the loop diagram never

executed and you need a default value). Even if you don't modify the value

it can still help prevent copies because left/right shift register

terminals are always "in place" to each other (meaning they must use the

same memory).

You can use the "Show Buffer Allocations" tool (Tools->Profile->Show

Buffer Allocations...) to see where copies are being made. They're shown

as little black dots (hitting the Refresh button makes them flash so

they're easier to see; yes, this could use some improvement). If you

modify your VI be sure to compile it (either run or ctrl+click the run

arrow) and then click Refresh to get an updated view of the dots/copies.

You can also use the Inplace Element Structure (introduced in 8.5) in some

cases to ensure that a value isn't copied. For instance, if you pull a

value out of a cluster, modify, and then put it back in the same cluster,

then you can avoid copying the cluster using the Inplace Element

Structure. Likewise, you can avoid copying an array element if you fetch

an element, modify it, and put it back in the same place.

There's a presentation online (just slides) that covers some of this with

some screenshots. You can view it here:

http://ftp.ni.com/pub/events/labview_dev_ed/2008/improving_performance.pdf

--

Adam Kemp

adam.kemp@ni.com

(512) 683-6058

When the VI executes as a subVI, LabVIEW has to put some value on the output indicators. regardless if the indicator is inside or outside the loop, the default values are used anyway right? So I think you have more control of the values if they are outside the loop, then you can place constants to define your outputs in the unused cases.

I agree. But as Crelf pointed out - if its a GUI then of course you are going to have FP objects inside the case structure.

But its not then, yes, I too prefer to have them outside for a couple of reasons:

I can define optional default values - or just select "use default if unwired". This is important e.g. on a basic SubVI with Errors I can pass the reference through the "in error" case, and not lose it (just like the LVOOP templates)

It is much neater and easier to read (for me) - I can see what goes in (LHS) and what goes out (RHS) as nothing is hiding anywhere.

Leads to optimised code (as per Adam Kemp's post)

It helps me maintain a standard interface to the code module. If I am not building a class, but a standard Multi-Functional VI (MFVI) I usually have 1 input and 1 output - normally type-def cluster (sometimes variants) to the module for data (and also the Command Enum and error in/error our clusters). This makes the interface flexible, maintainable and scalable. And I don't need to handle a bunch of default values. Wrapper VIs bundle the inputs into the interface and they also pull out the required outputs from the interface for that method call into the connector pane. So the API is just like a basic SubVI to use. Any used values are disregarded.

  • Like 2
Link to comment

Are you sure about that? I'm sure I've read that putting input controls inside a case statement prevents LV from making certain compiler optimizations. I can't find the reference at the moment though.

This might be what you're thinking of?

Thanks for all the answers.

For a second there, I thought we were going to have a flame war between Chris and Michael :)

Yes, Crelf, I probably was mistaken in referring to it as a simple state machine. It is more like a functional global in it's own architecture, although I don't usually think of it that way. The shift registers are used as persistent memory for its own internal use, rather than trying to provide global-like behavior for the callers.

Edited by Gary Rubin
Link to comment
For a second there, I thought we were going to have a flame war between Chris and Michael smile.gif

Not at all :) In fact, I think this is a really important discussion, and the varied answer are, well, all right. As I said, it really depends on what you're trying to achieve.

Link to comment

Here is a great piece of knowledge base from Adam Kemp. Twas posted on Info LabVIEW earlier this year.

It maybe the reference you are thinking of? The PowerPoint is a good read.

This might be what you're thinking of?

Yes on both counts. (I need to figure out a way to collect and organize all the tidbits of information I gather.)

Link to comment

In fact, I think this is a really important discussion

I agree, and I really appreciate the link to the Adam Kemp message. You (crelf) might have more access to the ins-and-outs of LabVIEW, but for the rest of us average Joes, all we can do is make observations about what works best in our own little code sets, and hope that those observations about the best way to do things can be generalized and applied to other cases. Some insight from NI can help us better understand why something works well, and therefore, when to use it.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.