You need to be INCREDIBLY careful when setting priorities on VIs and subVIs - almost to the point where if you don’t understand how LabVIEW generally treats VI priority (and how RT can affect that priority) then it’s probably not something you really want to be messing with. Why do I say this? Well, on RT you have to realize there are a ton of priorities - and different OS functions are being assigned to the different priorities. If you set the subVI to a higher priority than the OS functions, then you will effectively starve those OS functions - that may be file I/O, communications (network I/O), or you may even preempt the scheduler (which means NOTHING else will run while your subVI is running).
Often when someone asks how to play with priorities, what they’re really asking for is a lesson in control software architecture. Unfortunately I cannot do that over a short ChiefDelphi message post, but I can give you some tips that may help.
First I just want to warn you (again) that if you’re wanting to set VI priority, you’re likely doing it wrong. Let’s cover the priorities in descending order:
TIMED LOOP
CRITICAL
HIGHEST
HIGH
NORMAL
BELOW NORMAL
Timed Loop is its own priority, because it sits as the “Top Dawg” in the priority list - anything in the timed loop is going to run above everything else. Anything in the timed loop had better be really stinking important, because you’re starving literally everything else in the system. Timed Loops have priorities within the timed loop, so that you can have layered priorities within the timed loops - again, it had better be really dang important. And you had better be really careful about timing the timed loop, because while the loop is running everything else in the system is dead - let me say that again, EVERYTHING in the system is being starved out. NO - for example, you can’t call networking I/O and cause a priority boost for the networking I/O you called. As a matter of fact, if you’re doing anything in a Timed Loop except raw calculation or signal acquisition and processing - and not using a Real-Time FIFO - you’ve done something wrong and you should stop using Timed Loops (don’t do anything that could be considered blocking). Unless you know why this is important, stop using a Timed Loop. You can avoid starvation by scheduling the timed loop to run at intervals that leaves “dead time” in the timed loop so that other things can run (no, I do NOT mean “wait for ms”). If you don’t fully understand the timed loop, don’t use it, it is too powerful.
Critical priority is for TIMED_CRITICAL tasks. Things that have to run over everything else (except Timed Loops). This had better be important, because you’re starving everything else in the system out. If you’re doing File I/O, network I/O, or anything that is non-deterministic or blocking like that you’ve made your first mistake (no blocking calls!). Don’t make another. Big name companies with really smart people have boosted their VIs to Critical priority and not understood the ramifications and have had it cause nightmares in their control algorithms. Don’t do it, please. Priority inheritance from critical level VIs calling non-critical VIs is something that is impossible (okay, maybe just impossibly mind numbingly difficult) to debug.
Highest priority threads is the highest thread-level priority that threads can generally be given (without hosing the system). However, Highest priority threads are where most OS level functions are called, so it’s still possible to starve OS functions if you’re not careful. Networking I/O generally gets run at this level, but usually one level of OS priority above “Highest” - just don’t do anything super crazy like File I/O at this level unless it’s super-duper important to log that data, because you’re likely to block anyway.
High Priority is for VIs that need a little “extra” boost in priority. While this VI is running, no other lower-level VIs will be scheduled. Try not to spend too much time within High priority VIs, and don’t expect them to run “side by side” with normal level code - because it isn’t going to happen.
Normal priority is where you’re at by default. Pretty much everything by default runs at normal priority. Nothing else really needs to run at a higher priority, if you feel it does then more than likely you need to reevaluate your code architecture and find a way to run your higher-priority-needing code in some other branch/thread. There are valid reasons to run code at priorities above Normal priority, and even some REALLY GOOD reasons, but until you’ve “become one” with LabVIEW you likely won’t recognize the reasons until they’ve been pointed out.
Below Normal priority is where low-priority tasks usually run. On Real-Time systems, this is sometimes where File I/O is even run. The reason is because File I/O is not deterministic (just like network I/O isn’t) and the actual “meat” of the File I/O is done in the below-normal priority (the File I/O request from hardware triggers interrupts, and the interrupts are cleared in the hardware interrupts, but the actual work writing to disk is queued to a below-normal level). Anything running at normal priority will starve below-normal priority tasks.
One VERY IMPORTANT thing to note about LabVIEW is that even though there are priorities, a loop in LabVIEW doesn’t HAVE to round-robin to other threads of the same (or lower) priority. This is why you should NEVER EVER create a loop in LabVIEW without blocking calls in it (that actually block). Almost all loops in LabVIEW need some kind of “wait for ms” or “wait for ms multiple” to throttle the loop, yield execution to other loops of the same (or lower) priority, or similar - otherwise loops will literally eat 100% of the processor and starve out other threads in the system. Look at the Periodic Tasks VI, and you’ll see what I mean - the loops in that VI run in parallel to the Main VI, and are throttled by the wait calls in the loops. If you created a loop with no wait calls, and there were no blocking calls in the loop, you’d likely cause your cRIO to lose networking communications, become incredibly laggy, and even become unreliable.
I haven’t even talked about LabVIEW Execution System threads and how the LabVIEW diagram is broken up and run in different threads (via a method we call “clumping”), which really helps to explain how many simultaneous tasks REALLY CAN operate at the same time. Understanding the LabVIEW Compiler can go a long way into understanding how to write efficient code.
I hope I’ve made you reconsider why you think you need to deal with priorities, and instead maybe what you want is to push the PID code somewhere else so it can be run in a loop that is just running <FASTER> but not necessarily at a higher <PRIORITY>.
-Danny