Jump to content

When do you use Subroutine priority?


Recommended Posts

There is a VI execution priority setting called subroutine. Explainations of this setting can be found here:

While I understand some of the basic principles of the subroutine setting, I'm not 100% certain about the best practices and rules of thumb for when to apply this setting and when to avoid it.

Does anyone have any thoughts on this matter?

Note that this questions applies to a design decision in a new OpenG candidate.

After this discussion, I'd be happy to consolidate the information into a Subroutine page on the LabVIEW wiki.

Link to comment

QUOTE (Jim Kring @ Apr 1 2008, 01:37 PM)

While I understand some of the basic principles of the subroutine setting, I'm not 100% certain about the best practices and rules of thumb for when to apply this setting and when to avoid it.

Does anyone have any thoughts on this matter?

We have gotten good results using subroutine priority to speed up some fairly memory-intensive subvis. Our processing is inherently not very parallel, so we don't have to worry about the subroutine priority VI's stealing CPU time from each other. They inherit the thread from their caller, so if you want to do things in parallel, you might have to be careful about having too many subroutine subVI's called by the same caller.

Aristos, or someone else with more knowledge might correct me on this, but I think that our performance improvement may be due to the fact that our subroutine subVI's handle memory more effeciently due to the fact that they don't have to allocate anything for the front-panel controls and indicator.

Gary

Link to comment

QUOTE (David Wisti @ Apr 1 2008, 12:08 PM)

That's an interesting node setting -- I wasn't familiar with it, but found some info http://zone.ni.com/devzone/cda/tut/p/id/4682' target="_blank">here. From what I gather, it's probably most useful for inter-process communication -- optimizing the calling application rather than optimizing the performance of the subVI/subroutine being called.

Link to comment

the rule of thumb that I try to use is

"if it is very simple, already always executes relatively quick, has no other interaction w/ other parts of program and could give me speed benefits by utilizing my entire processor."

Then I seriously consider changing it to a subroutine.

Personally I do not feel comfortable EVER saying skip any part of code that I put in, unless it's a case in a structure.

Link to comment

QUOTE (Jim Kring @ Apr 1 2008, 02:32 PM)

That's an interesting node setting -- I wasn't familiar with it, but found some info http://zone.ni.com/devzone/cda/tut/p/id/4682' target="_blank">here. From what I gather, it's probably most useful for inter-process communication -- optimizing the calling application rather than optimizing the performance of the subVI/subroutine being called.

I use it for one-way communication between "non-time critical" to "time critical loops" where the skips are in the time-critical loop. This is one way to prevent priority inversion. Since the creation of RT FIFOs, I barely use this technique anymore.

Link to comment

QUOTE (Michael_Aivaliotis @ Apr 1 2008, 04:20 PM)

I never use subroutine priority. I would like to see some benchmarking. Like many things in LabVIEW, such as thread priorities, the benefits of playing with them are highly overrated.

Michael,

I know this doesn't qualify as a benchmark, but I have notes that using subroutine priority sped up one of my pretty large VIs by a factor of 10 (from ~13ms to ~1ms) as reported by LabVIEW's profiler. A caveats - I don't know how well the profiler reports on subroutine priority things.

This particular VI and its subvis have at least one 20k x 23-element matrix plus lots of 2k and 20k vectors running through and between them. I assume that the big performance boost had to do with subroutine priority handling memory allocation differently.

Gary

Link to comment

QUOTE (Jim Kring @ Apr 1 2008, 09:37 AM)

Hi Jim,

We touch on threading and execution priorities in "LabVIEW Graphical Programming". :rolleyes:

"The one exception to the time-slicing feature of each execution system is when a VI is set to run at subroutine priority. A subroutine VI will always run to completion within its execution system. A thread running a subroutine VI can still be preempted by the RTOS but no other VI running in the same execution system as a subroutine VI will be able to run until the subroutine VI is finished."

Johnson, Gary W. "LabVIEW Graphical Programming" (McGrawHill, New York, 2006) 391.

I use it occasionally to optimize a crucial VI, or protect a critical resource, but as Michael pointed out, usually it's better to use LabVIEW's defaults. Repeatedly calling a subroutine VI can negatively impact execution.

just my $0.02,

Richard

Link to comment

QUOTE (Michael_Aivaliotis @ Apr 1 2008, 01:20 PM)

I never use subroutine priority. I would like to see some benchmarking. Like many things in LabVIEW, such as thread priorities, the benefits of playing with them are highly overrated.

I know that the subroutine priority can yield huge benefits (N-fold improvements in performance) when used appropriately. What I'm trying to pin down are the guidelines for when you'll see such improvements, as the misuse of this setting could certainly result in performance degradation.

Link to comment

QUOTE (Jim Kring @ Apr 1 2008, 04:29 PM)

I know that the subroutine priority can yield huge benefits (N-fold improvements in performance) when used appropriately. What I'm trying to pin down are the guidelines for when you'll see such improvements, as the misuse of this setting could certainly result in performance degradation.

Well it's all relative. Now with LabVIEW being always multithreading even inside a single execution system the negative effect of subroutine VIs is not as dramatic as it used to be in old single thread LabVIEW days. At that time subroutine priority was specifically reserved for small VIs that could be relatively quickly executed. LabVIEW optimized the calling context in such a way that there was very little overhead in calling a subVI similar to if the VIs diagram would have been directly embedded in the caller. This could lead to rather huge speed improvements because the calling overhead and the chance for memory copies could be greatly reduced. At the same time while a subroutine was busy NOTHING else in LabVIEW could be going on, since that subroutine exclusively blocked the one single thread LabVIEW had at that time. So if you did that to a lengthy function, LabVIEW could seemingly freeze entirely.

With Post LabVIEW 5 multithreading this setting has both been less important as well as having a lesser bad inpact even for lengthy functions. Since there are many threads LabVIEW is using even a blocking subroutine will not block the entire program (unless you happen to run the subroutine in the UI system). At the same time LabVIEW has made many memory optimization improvements so that the advantage of a VI being sort of inlined does not likely yield a big effect there anymore.

What remains is the reduced caller overhead for a subVI. So the thumb of rule would be:

Use subroutine only for very small subVIs whose execution speed is rather short and that gets called very very often. Because for a VI that takes 1s to execute shaving of a microsecond in calling overhead is simply useless, but if that subVI itself only consists of a few LabVIEW primitives taking up anything in the order of 1 microsecond to execute, adding another microsecond for the calling overhead will be significant. But even then if you do not call that VI millions of times inside a loop it is not likely to buy you much.

Rolf Kalbermatter

Link to comment

I'm working now on sth like raytracer and I use subroutine priority for all subVI's performing geometrical computations such as kd-tree searching, intersections calculations, operating on vectors, etc. Inside these VI's I also use many DLL calls. I noticed about 30% speed increase comparing to normal priority. I also performed some benchmarks trying various solutions for ordinary dot product calculation and one using subroutine was fastest. I have this benchmark somewhere if someone is interested.

BTW, I found recently a key in labview.ini file which enables "Inline SubVI" option in context menu. I inlined all subVI's of my main kd-tree searching VI (also all nested ones) and it resulted in 150 times (!!!) speed increase. I thought it was already optimized... I suppose it is because closing a part of code in subVI is a barrier for most optimal data-flow (waiting for all signals before executing subVI). But this is for another discussion...

Link to comment

We experienced a decrease in speed using VIs with subroutine priority in two different timed loops (on a dual core machine).

Removing the subroutine priority got us back on track.

If I remember correctly, some of the VIs were reentrant, which makes it much harder to understand why the subroutine priority affected performance.

/J

Link to comment

QUOTE (rolfk @ Apr 2 2008, 02:33 AM)

<snip>

Use subroutine only for very small subVIs whose execution speed is rather short and that gets called very very often.

Rolf Kalbermatter

Sounds like this setting would be ideal for Functional Globals or Action Engines. Is this thinking correct?

Link to comment

Probably an old technique which isn't compatible with latest versions of LabVIEW, but...

Years ago I had an application where I needed to send a trigger message out on a number of serial ports as near to simultaneously as possible. I did this by sending the last character of the trigger message using direct i/o write to the uart registers one after another in a tight loop. I found that by putting routine which did this port i/o at subroutine level it was less likely (but still happened rarely) that the os took over and did time-consuming routine housekeeping in the middle of sending the message to the various ports.

Not even sure if you can embed port i/o in subroutine vi's anymore, so this technique from the past may be nothing more than a historical footnote to the current topic, but FWIW, that is the only time I've used subroutine priority with any useful success.

Best Regards, Louis

Edit: minor typo

Link to comment

QUOTE (pallen @ Apr 2 2008, 08:25 AM)

Sounds like this setting would be ideal for Functional Globals or Action Engines. Is this thinking correct?

I don't think they qualify for the "very very often called" item. At least not as I design them. If there is an operation that would require that functional global being called millions of times inside a loop I usually create a new method that does that particular operation inside that functional global instead. That should take care of that.

Rolf Kalbermatter

Link to comment

To add another piece of old documentation to the mix, take a look at this under "SubVI Overhead":

QUOTE

When you call a subVI, there is a certain amount of overhead associated with the call. This overhead is fairly small (on

the order of tens of microseconds), especially in comparison to I/O overhead and display overhead, which can range

from milliseconds to tens of milliseconds.

However, this overhead can add up in some cases. For example, if you call a subVI 10,000 times in a loop, this overhead

might take up a significant amount of time. In this case, you might want to consider whether the loop can be embedded

in the subVI.

Another option is to turn certain subVIs into subroutines (using the VI Setup Priority item). When a VI is marked as

a subroutine, the execution system minimizes the overhead to call a subVI. There are a few trade-offs, however.

Subroutines cannot display front panel data (LabVIEW does not copy data from or to the front panel controls of

subroutines), they cannot contain timing or dialog box functions, and they do not multitask with other VIs. Subroutines

are short, frequently executed tasks and are generally most appropriate when used with VIs that do not require user

interaction.

Link to comment

QUOTE (rolfk @ Apr 2 2008, 09:50 AM)

I don't think they qualify for the "very very often called" item. At least not as I design them. If there is an operation that would require that functional global being called millions of times inside a loop I usually create a new method that does that particular operation inside that functional global instead. That should take care of that.

Rolf Kalbermatter

Maybe I'm not thinking of them quite the same way then. Although I suppose if I was using a FG to pass data to my UI, that FG would only be called once per iteration of the UI loop. So even though I may call the FG millions of times between my UI and DAQ loops, they're only getting called once per loop and therefore wouldn't see much benifit from running as a Subroutine?

Link to comment

QUOTE (pallen @ Apr 2 2008, 10:39 AM)

Maybe I'm not thinking of them quite the same way then. Although I suppose if I was using a FG to pass data to my UI, that FG would only be called once per iteration of the UI loop. So even though I may call the FG millions of times between my UI and DAQ loops, they're only getting called once per loop and therefore wouldn't see much benifit from running as a Subroutine?

Yes if you run the application hours and hours they will eventually be called million of times but with very very often I meant millions of times in short time (seconds). Anything in the context of UI should not be optimized in terms of microseconds but rather in the way it is done (more optimal algorithme to prepare data, avoid huge memory copies, defer panel updates during bulk UI updates, etc).

Rolf Kalbermatter

Link to comment

QUOTE (Gary Rubin @ Apr 1 2008, 09:56 PM)

Speaking of which - I remember Greg McKaskle from info-labview. Whatever happened to him. I assume he left NI?

He's still at NI (or at least he was a few months ago. I don't suppose that changed), but he just stopped posting online other than very rare appearances to the NI forums.

Link to comment

QUOTE (rolfk @ Apr 2 2008, 12:33 AM)

So the thumb of rule would be:

Use subroutine only for very small subVIs whose execution speed is rather short and that gets called very very often. Because for a VI that takes 1s to execute shaving of a microsecond in calling overhead is simply useless, but if that subVI itself only consists of a few LabVIEW primitives taking up anything in the order of 1 microsecond to execute, adding another microsecond for the calling overhead will be significant. But even then if you do not call that VI millions of times inside a loop it is not likely to buy you much.

One thing that I wonder, is whether OpenG primitive-like VIs that meet these criteria should be configured as subroutines. Will this help more people than it hurts?

Is it better to...

A) leave them as non-subroutine and let people who need the extra performance use local copies that are marked as subroutines

or...

B) make them subroutines and let people who are negatively affected use local copies that are non-subroutines

It would seem that the number of people that would be helped by the subroutine setting is far greater than the number of people that would be hurt by the subroutine setting. Am I off base, here?

Thanks,

-Jim

Link to comment

QUOTE (Jim Kring @ Apr 2 2008, 02:57 PM)

One thing that I wonder, is whether OpenG primitive-like VIs that meet these criteria should be configured as subroutines. Will this help more people than it hurts?

Is it better to...

A) leave them as non-subroutine and let people who need the extra performance use local copies that are marked as subroutines

or...

B) make them subroutines and let people who are negatively affected use local copies that are non-subroutines

It would seem that the number of people that would be helped by the subroutine setting is far greater than the number of people that would be hurt by the subroutine setting. Am I off base, here?

Thanks,

-Jim

I think your reasoning is way to general. There are functions that might benefit reasonably well from subroutine priority but many others that will see little benefit in typical applications. The first could be some of those little helper functions such as Valid Path. The latter would be for instance things like Delete Recursive and such. It's all about is it a function that takes always little time to execute and is likely to be called inside loops many many times. If not the advantage of a little faster execution is IMHO not at par with the disadvantage of causing possible problems that might be also hard to debug since debugging of subroutine VIs is not possible at all.

In general speed of an application is not lost in the calling overhead of VIs at all but in the type of algorithme used and even if calling overhead of VIs can add some significant performance loss it is probably about much less than 5% of the VIs that can significantly add to the performance by reducing their calling overhead. Penalizing the other 95 to 99% of the VIs for that is not a good option for me.

Rolf Kalbermatter

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