From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Preallocated Reentrant VI within Parallelized For Loop

Solved!
Go to solution

@nathand wrote:

Kevin_Price wrote:

I still wish this could be done with simpler syntax as I tried to illustrate in reply #18.


The simpler syntax way to do this is to pass the state information into the VI, rather than having the VI remember its state. Then you can use shared clones without any issues, and let LabVIEW determine how many instances to parallelize. Maybe that's not possible for some reason in your specific function, though.


Correct, this would not work with the specific case that we are dealing with because the subVIs within the parallel VI have their own memory space that they create themselves. This means, you can't take that information and feed it back to them yourself.

Cheers


--------,       Unofficial Forum Rules and Guidelines                                           ,--------

          '---   >The shortest distance between two nodes is a straight wire>   ---'


0 Kudos
Message 31 of 40
(2,152 Views)

James.Morris wrote:

Would someone please summarize the solution along with which correct option flags to use, so I can mark that as the solution?


Trying to make this as succinct as possible, but if it's too brief, elaborate on the question and I'll elaborate on the answer 😉

 

Let's say you unrolled the for loop, meaning you replaced it with an index array (with one output per array element) and placed a corresponding instance of the subVI for each array element on the block diagram, removing any dynamic calls. Then consider how you would configure reentrancy of that subVI. If you would configure it as preallocated clones, then you should use the 0x08 flag. If you would configure it as shared clones, then you can use 0x40. If you're not sure about the difference, that's a separate question.

 

The asynchronous behavior flags are orthogonal to the flags that determine reentrancy. An asynchronous call is not the same as a parallel or concurrent call. What you have here are parallel synchronous calls, so you should not use the asynchronous flags.

Message 32 of 40
(2,134 Views)

Nathan's your contributions to this thread are massive.. The one point you seem to be missing is that the 40 flag ignores the reentrancy settings of the vi while 8 expects to call a reenterant vi.  

 

A really fun thread! 


"Should be" isn't "Is" -Jay
0 Kudos
Message 33 of 40
(2,129 Views)

@James.M wrote:

Thanks for all the discussion, guys! I think the solution, whether it's easy or not, involves multiple instances of a VI using a Run by Ref node.

 

Would someone please summarize the solution along with which correct option flags to use, so I can mark that as the solution?



We are trying.  Its not simple.


"Should be" isn't "Is" -Jay
0 Kudos
Message 34 of 40
(2,128 Views)

@JÞB wrote:

Nathan's your contributions to this thread are massive.. The one point you seem to be missing is that the 40 flag ignores the reentrancy settings of the vi while 8 expects to call a reenterant vi. 


No, either way the VI must be configured as reentrant. From the help:

 

The 0x40 option flag is valid only in the following situations:

  • The target VI is reentrant and shares clones.
  • The target VI is reentrant, and you also include either the 0x80 or the 0x100 option flag.
0 Kudos
Message 35 of 40
(2,126 Views)

@nathand wrote:

 

If we were making a movie, the 0x08 flag references would be named actors, and the 0x40 references would be anonymous extras. When you need a particular actor you'd use the 0x08 reference, whereas the 0x40 reference would simply say "get me anyone who's available to walk across the set."

 


I think that's a wonderful explanation, even if it is a little simple to stand alone.

0 Kudos
Message 36 of 40
(2,091 Views)

@James.M wrote:

@nathand wrote:

Kevin_Price wrote:

I still wish this could be done with simpler syntax as I tried to illustrate in reply #18.


The simpler syntax way to do this is to pass the state information into the VI, rather than having the VI remember its state. Then you can use shared clones without any issues, and let LabVIEW determine how many instances to parallelize. Maybe that's not possible for some reason in your specific function, though.


Correct, this would not work with the specific case that we are dealing with because the subVIs within the parallel VI have their own memory space that they create themselves. This means, you can't take that information and feed it back to them yourself.


 

I guess the way I look at it is that having to pass state info in and out doesn't strike me as simpler.  It simply defers the complexity of managing state info to the next level up in the call chain.  

 

At an earlier point in my attempts to do this thing, I went digging down into some of the waveform filter vi's b/c I knew that they were reentrant while also operating properly on arrays of waveforms.  What's down inside there is exactly this kind of thing where there's a separation between the calculation part of the algorithm and the state-variable storage part.  The state storage lives at a level above the calculation part and passes state variables into and back out of the calculation.

 

In principle, I guess that just about any reentrant function that must retain state information could be re-written as a hierarchy of 2 functions. The lower level function takes in state info, performs its calculations, and passes out the new state info.  The higher-level function maintains the USR's for state storage and provides the public api to the function.  Since the lower-level function completely depends on a well-behaved caller, this kind of approach probably works best inside a library where the lower level core function is private and the higher level api function can be public.

 

Anyway, it's been good food for thought.  I may try this method of rewriting my processing functions into a hierarchy of 2 functions inside a library for my app.  I have several parallel threads that need to keep their own separate instances of these functions.  My current VI server approach requires me to pay the overhead of managing VI refs for each function in question within each thread.   The 2-level hierarchy would only require that kind of overhead when I first make the library; thereafter I can just drop the public api function down on the diagram -- which was what I was asking for in the first place!

 

So I guess I started by resisting the approach but have kinda come around to (mostly) talk myself into it now.  Thanks for spurring the thought process, nathand.

 

 

-Kevin P

 

 

P.S.  Note for clarification: for my uses, I'd still want the upper level api function to be a preallocated reentrant.  The lower level private function would probably be fine as reentrant with shared clones.  It would even work if it wasn't reentrant at all, though that would make the upper level public api instances get in line for serialized access.

 

 

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
0 Kudos
Message 37 of 40
(2,085 Views)

I'm bumping this one because the topic of this thread is really close to my question.

First: This thread contained a darned whole lot of insight into how LV handles VI clones when using the different ways of calling a VI. Top job, people.

 

Now, onto my question:

I'm currently building a kind of VI library with functions I frequently reuse in all kinds of applications, usually some string searching, pattern matching, sequentialized TicTocs - that sort of thing. None of the VIs need to maintain a state of information from an ealier call, they just perform their task, give the result and that's it.

Now, which reentrancy setting should I use for those (as I intend to create a packed library and I will (must) not be able to alter them at will afterwards)? Inlining, where possible, is used all of the time, of course.

I figure, preallocated clones should be best, but for parallel loops whose number of iterations is determined at run-time - how does LabVIEW determine how many clones have to be allocated? With shared clones it's obvious - it allocates whenever all clones are busy, thus the jitter in execution time.

0 Kudos
Message 38 of 40
(1,380 Views)

Sounds like these functions shouldn't be reentrant. Most likely they execute fast enough that there's no reason to attempt to run multiple copies in parallel. An interesting test would be to set one of these VI to share clones and count how many clones are created; if it's not more than one or possibly two, why bother with reentrancy?

 

To answer your question about parallel loops - you can configure the number of parallel executions, and that should be the number of clones created. However, if there aren't enough pre-allocated clones, LabVIEW creates new ones as with shared clones.

0 Kudos
Message 39 of 40
(1,373 Views)

I only half-agree. As some of the functions contain regular expression matching, those could very well slow down a critical application part, especially when integrated into a loop. Of course, those other functions like tictoc do not have to be reentrant.

 

Your point about parallel loops being able to be configured - I can kind of see that. But the hint about allocating on the fly would be exactly what I would want to avoid with a possibly clever reentrancy setting. So unfortunately there would be no difference between shared and preallocated clones when I don't configure the processor connector.

 

Thanks for your help.

0 Kudos
Message 40 of 40
(1,359 Views)