05-22-2023 04:35 PM
Every time I File>New VI knowing that the VI I’m about to make can or may be must, be reentrant, I find myself hemming and hawing over what to do at the selection screen in the vi properties. I understand the basics between non-reentrant and reentrant, and even the basic gist between shared and pre-allocated, but I definitely don’t know which is best to pick each time or all the detailed consequences of each.
For instance, let’s say a certain subvi is a very simple concatenation, array operation, or mathematical operations. And suppose this will be needed in numerous places all over the program, called by various non-reentrant parents. My first instinct would be to preallocate and check the inline box too. There might even be a good argument against reentrancy if performance is not critical.
Now let’s say the vi I wish to make reentrant is something I want to call via Run vi, call asynchronous, etc. Here’s where things break down for me. There are numerous combinations of reentrant call options and I think they matter depending on which reentrant box you’ve checked but I don’t know all the particulars. I generally choose shared clone when I don’t know how many clones to expect at runtime. Is that the ultimate criteria? What are the consequences of preallocated clones called in this way? Which prepare reentrant calls work for which reentrancy type? Is it possible for a calling vi to access a specific clone via its reference with both types?
would also love some rules of thumb for when and why to inline a subvi.
05-22-2023 05:49 PM
@DoctorAutomatic wrote:
Every time I File>New VI knowing that the VI I’m about to make can or may be must, be reentrant, I find myself hemming and hawing over what to do at the selection screen in the vi properties. I understand the basics between non-reentrant and reentrant, and even the basic gist between shared and pre-allocated, but I definitely don’t know which is best to pick each time or all the detailed consequences of each.
For instance, let’s say a certain subvi is a very simple concatenation, array operation, or mathematical operations. And suppose this will be needed in numerous places all over the program, called by various non-reentrant parents. My first instinct would be to preallocate and check the inline box too. There might even be a good argument against reentrancy if performance is not critical.
Now let’s say the vi I wish to make reentrant is something I want to call via Run vi, call asynchronous, etc. Here’s where things break down for me. There are numerous combinations of reentrant call options and I think they matter depending on which reentrant box you’ve checked but I don’t know all the particulars. I generally choose shared clone when I don’t know how many clones to expect at runtime. Is that the ultimate criteria? What are the consequences of preallocated clones called in this way? Which prepare reentrant calls work for which reentrancy type? Is it possible for a calling vi to access a specific clone via its reference with both types?
would also love some rules of thumb for when and why to inline a subvi.
Good questions! I am going to participate in this topic as a learner.
05-23-2023 06:14 PM - edited 05-23-2023 06:30 PM
This is a good question that is not getting much discussion in the forum so instead of a direct answer I have some thoughts. I think this is a good place to start (from the labview wiki: https://labviewwiki.org/wiki/Reentrant_VI)
To expand on this, reentrant means that more than one execution is allowed to take place at the same time. In other languages, it is more a situation than a setting. You never mark a C function as allowing or disallowing reentrancy, it is either safe to do so or a source of bugs. In LabVIEW it is a setting, and many times its setting doesn't affect the correctness of a VI, but in some cases, it can be a source of bugs. It depends on what the VI does.
In particular the part about correctness is interesting, as in there is not a correct way there is only a way that does not cause bugs : )
__________________________________________
I have a thought process for selecting re-entrant VIs that goes like this, not saying it is correct, just getting the conversation going:
1) Will it be called recursively? if yes its re-entrant , try pre-allocate
2) will it be called recursively in a loop? if yes its re-entrant , try pre-allocate
3) is it a .vit that will need a few instances? if yes its re-entrant, try shared
4) is it an actor? don't worry about it
I read somewhere that you can only have the number of shared clones per the number of processors you have on your machined, or that is what think I remember about that.
I guess 'pre allocate' and 'shared' are compiler instructions that gets down into C/C++ land and can choose to either execute the 'VI' in an concurrent thread or another process.
Perhaps that is a way to think about it, if you need to run re-entrant code in parallel, it's better to be shared, if you have recursive calls, you cant run it in parallel so its better to pre-allocate.
I think 'shared' is bad terminology for a VI because it makes me think shared memory space, which might be true for the instructions but probably not for the state.
05-23-2023 10:39 PM
I (slightly) beg to differ (based on several "things which worked for me" examples).
I've written routines that did recursion, and that did parallel detached clones. I thought I had more examples where I used recursion, but I'm at NI Connect, and can only look at a subset of my LabVIEW "old stuff" and can't find my other "clever" recursive routines (like quickly and efficiently calculating binomial coefficients, which I have somewhere ...).
Bob Schor
05-24-2023 12:15 AM
Hi
NI has made a pretty detailed explanation for how to manage reentrancy.
See this :
https://www.ni.com/docs/en-US/bundle/labview/page/lvconcepts/reentrancy.html
Regards
05-24-2023 01:01 AM
Hi again
NI moves everything to their web site manual and timestamp it as new ( 2023 ).
The web version is lifted from a help file named lvconcepts.chm, included with every LabVIEW release, at least since 2010 and maybe even before that.
The content of this help file has changed over time. So looking into older versions of that file may give additional insight angles into the subject.
I prefer the older CHM versions. They look better.
Regards
10-18-2024 03:39 AM
The following is a screenshot of the excellent summary table included in the Reentrancy: Allowing Simultaneous Calls to the Same SubVI LabVIEW User Manual article that Yndigegn shared above.
10-19-2024 05:12 AM
@Yndigegn wrote:
I prefer the older CHM versions. They look better.
Definitely agree, they looked comprehensive, easier to get an overview at a glance, typographically easier on the eyes, and much better organized on a page. The new format may be flow design compatible but as a consequence displays neither well on a big screen nor normal screen, a tablet or a mobile phone. I guess it’s democratic at least, everybody gets a messy looking page layout.
it also seems nearly impossible to add any graphics to such documents, at least they were pretty much all pruned from the documents when porting them to the new format.
10-20-2024 11:00 AM
I was just notified that there was a response to this old topic which brought to mind a related idea that I have been mulling in my head over the years. So here it is:
Let's say you have a need for a reentrant vi that will be called by many other VIs, let's say you make it shared clone reentrant. It seems critical, in my mind, that all sub functions, subvis, or calls made within this VI are reentrant also. Otherwise, all clones will still wait at whatever subvi is not reentrant, sort of cancelling this advantage of reentrant behavior. I'm particularly thinking of this in terms of say a VI used for database transactions. It is my understanding that the way in which the DB connectivity module is ultimately non-reentrant at the point of interaction with the database drivers. So if your reentrant clone vi was supposed to aid many modules of your application in accessing a database, they will still block each other at some point when trying to call the database drivers and you will be hardly better off than just having a nonreentrant vi. Am i thinking about this the wrong way? Am I incorrect on certain facts here?
10-20-2024 02:25 PM
You are correct. I can't speak about the specific case of the LabVIEW Database Toolkit VIs, but, in general, VIs maintain their re-entrancy settings regardless of the re-entrancy setting of the caller. This makes sense, when thinking that in the same application the same VI may be used by different callers, with different re-entrancy settings.
In other words, the re-entrancy setting of the caller is independent of that of the callee (the subVI). Changing the re-entrancy setting of the caller does not automatically change the re-entrancy setting of the subVIs. AFAIK there is no way to force the re-entrancy setting of a VI to propagate to its subVIs.
All this means that, yes, a re-entrant caller VI that contains non-re-entrant subVIs is likely to behave similarly as if the whole caller VI was non-re-entrant. This is because, while two instances of the caller VI can start executing in parallel, soon after that the multiple non-re-entrant subVI instances will start to block each other due to their non-re-entrant nature.
The solution would be to manually change the re-entrancy setting of all or most subVIs to one of the two re-entrant options: shared clone or pre-allocated clone. You might be able to do this even for the low-level LabVIEW Database Toolkit VIs, if they are not password protected.
"let's say you make it shared clone reentrant" - In general, pre-allocated is to be preferred to shared clone. I would advise that you set your subVIs to pre-allocated clone, not to shared clone. Doing so will mean that your application will use more memory, but will execute faster. When running on modern desktop or laptop systems with GB's of memory, using a few KB or even a few dozen MB of extra memory is virtually inconsequential. But the difference in execution speed may be noticeable.
In my opinion shared clone should be used in specialised use cases only. For example on memory-limited systems and for dynamic-dispatch override VIs (which are not allowed to be set to pre-allocated clone, so using shared clone is the next best thing).