Saturday, September 15, 2007

New Type of Reentrant VIs


In LabVIEW 8.5, there's a new option for reentrant VIs.

In the VI Properties dialog, on the Execution page, there is a checkbox for Reentrant execution. The "Preallocate clone for each instance" option is the same behavior that reentrant VIs had in previous versions of LabVIEW.

But the "Share clones between instances" option is new. This setting can help you use less memory, but only for reentrant VIs that do not maintain state between calls. Below is information from the feature specification that explains in greater detail.

The main consideration when determining how to set this option is whether the VI remembers some value from one call that must be the same for the next call from the same diagram location. The most common case of this is uninitialized shift registers but it can also be done through local variables, control properties or methods, or the First Call? function. Calling a subVI with this requirement can also cause all reentrant callers to have the requirement.

If the VI does not require state to be maintained, here are the other considerations for deciding how to set this option.
  • Memory usage
    • Preallocate clone for each instance - Clones allocated for every call even if none ever happen at the same time.
    • Share clones between instances - Clones allocated only for the maximum number of calls that happen at the same time.
  • Execution speed
    • Preallocate clone for each instance - SubVI call overhead is constant.
    • Share clones between instances - SubVI call overhead is slightly increased in all calls by the need to retrieve a clone from the cache. In some cases it can be more affected by the need to wait for a new clone to be created or another call to complete, whichever happens first.
  • Debugging ease
    • Preallocate clone for each instance - Breakpoints and probes can be created in a specific clone before the VI is ever executed.
    • Share clones between instances - Any breakpoints or probes in a clone will only affect calls that happen to use that clone. Best practice is to set breakpoints in the original which will be set in all clones or step into the call so the correct clone is debugged.
The behavior of strict VI references is not changed by this setting. Each strict VI reference allocates its own dedicated clone. This leaves the user's code in direct control of how many clones are created and when they are used.

Since the purpose of subroutine execution priority is to reduce execution overhead, reentrant subroutines are always preallocated.

I have also created a short video to illustrate the differences between normal, preallocated reentrant, and shared reentrant subVI execution. The video is posted on YouTube and as a Quicktime movie on my server.

Labels:

6 Comments:

Anonymous jlokanis said...

Regarding speed issues, it seems that the main thing to avoid is memory allocation at run time. Given this, is there ever a case where not preallocating would be faster? I understand the decreased memory footprint, but is it really a big enough decrease to make a difference? What conditions would have to exist to make it worthwhile to use this new mode?

Second, regarding debugging of reentrant clones, what are the best practices for locating and debugging a specific clone of the VI in a specific execution path. Especially if the entire execution tree consists of reentrant VIs of a top level dynamically spawned reentrant VI (daemon)? We have the option to open the Reentrant Original from a clone, but is there any way to find all clones of a reentrant VI and then from the clone, navigate up and down it's calling tree?

12:58 PM, September 17, 2007  
Blogger Christina said...

Performance questions are always a bit tricky. Although the primary benefit of shared clones is reducing memory usage, they can sometimes improve execution time, because using less memory can reduce the system's need to swap out to disk.

The additional overhead for a call using shared clones is relatively small. Unless you need to persist state information or have a large amount of parallel execution of the reentrant subVI, the new mode should work well for you.

I recommend using benchmarks for your particular usage if you're unsure which option is best.

Debugging reentrant VIs can get confusing. I can't recommend "best" practices, but here are some techniques:
- For preallocated clones, you can double-click on a given subVI to access the clone for that call (after the VI has been compiled). You can then set breakpoints for that instance.
- Once you are debugging a reentrant clone, you can use the "Call list" ring on the diagram window to navigate to the calling instances.
- You can save VIs with breakpoints in them. You could write conditional code that determines if it is the instance you want to debug and put the breakpoint in there.
- Similarly, you can write conditional code to open the front panel of reentrant VIs you want to debug, and then navigate to their block diagrams.
- For shared clones, you should set a breakpoint on the call to the reentrant subVI and use the "Step into" button on the diagram window's toolbar to debug the reentrant clone.

5:42 PM, September 18, 2007  
Blogger ruel said...

This post has been removed by the author.

10:53 PM, February 28, 2008  
Blogger ruel said...

it's late, i mistyped: here is what i meant to say:

how is the number of shared clones calculated? by the number of callers, or by the number of calls? suppose i have a caller VI which calls the subVI 8 times in series (such that that particular caller VI is never using the subVI at the same time) -- as opposed to calling the subVI 8 times in parallel and relying on dataflow. in each case, will one clone be allocated on behalf of that subVI, or will eight? thank you.

10:55 PM, February 28, 2008  
Blogger Christina said...

The number of shared clones is not calculated at compile-time. That's why there is a slight run-time overhead. For your first case, where the caller is never using more than one reentrant subVI at a time, you should only have the initial allocation (which I believe is two instances). For the second case, even though there are eight calls which can execute in parallel, they won't necessarily all be running simultaneously (depending on how they are scheduled). So, if you never have more than two running at the same time, you would only have two shared clones. If, however, a clone needs to run and there are no available shared clones, LabVIEW will allocate more. For performance reasons, it allocates more than one at a time, but I can't remember the exact algorithm offhand (but I'm pretty sure 8 is one of the sizes you'll get). So, to try to answer your question, in the first case you would have 2 shared clones and in the second you would have somewhere from 2 to 8.

8:19 AM, March 01, 2008  
Anonymous Anonymous said...

Hello
Nice to meet you
I'm from Tunisia. I'm looking for the possibility to have 'NI LabVIEW FPGA Module'. If you can help me you will make me too hapy.
Thanks a lot
Mansouri Sofienne

12:30 PM, August 15, 2008  

Post a Comment

<< Home