Our online shopping is experiencing intermittent service disruptions.

Support teams are actively working on the resolution.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

FGVs called from reentrant code

Solved!
Go to solution

I'm struggling to understand something about reentrancy, and would appreciate some advice.

I have made a small example program based on NI examples to demonstrate some of what I'm not understanding.

Say I have a DAQ program that someone has already written. I want to launch N instances and start and stop them simultaneously.

I chose User events to broadcast the start/stop messages to the instances and that all works fine. I have set the DAQ main VI reentrancy to preallocated clone reentrant execution and launch it exactly as in the 'Populating the Asynchronous Call Pool' NI built-in example, so I can have N clones. To each of these clones I feed in an identifier and different COM port for the DAQ, via the connector pane on the 'start asynchronous call VI'.

 

I assume that any subVI that doesn't have uninitilised shift registers I can set as reentrant, to avoid having to wait for the call to execute.
Q1: I understand this is good practice in general, but is it necessary here, or by virtue of the caller VI being reentrant, does it avoid the different instances having to wait for calls? Is shared clone the right choice for these VIs that do not store previous data?

Where it gets complicated is that the DAQ program has a number of FGVs and action engines. Some are called from one place in the DAQ main VI, some are called from several. Most should have a different memory space per instance, rather than being shared between instances.

Q2: In the simple example I only get 1 clone on the Shared Clone FGV, which seems to be shared between instances, exactly like a non-reentrant VI. This is not what I expected. Why is this? I don't plan to do this, I am just curious.

I understand that VI's with uninitialised shift registers shouldn't be reentrant. I was expecting to have to make some sort of instance manager for these stateful VIs e.g. that puts the data into a map with the instance identifier, and then pulls out the right data. The small example I made confirms this. But my actual program seems to 'just work' without me doing this, and I have been debugging and don't quite understand why. I want to make sure it doesn't start misbehaving.

Q3: Do I have to make an instance manager for each FGV? Why/why not? Any suggestions on a good ways of handling FGVs that are used in multiple places within an instance?

Q4: Are there any considerations for making the clones launch quickly? Even though the benchmarking takes 0ms, it is about 1s before the new windows start popping up on the screen, and it's much worse on my large DAQ program.

Answers to any of these questions would be much appreciated.

Kind regards,

Leah

0 Kudos
Message 1 of 4
(463 Views)
Solution
Accepted by topic author Leah_E

Hi Leah,

 

 

Q1: When you want to make a module "cloneable", turning its subVIs to reentrant is generally a good practice, especially for subVIs that can take several milliseconds to execute. It allows to make your clones more "parallelized" and avoids clones to block each other while waiting for access to non-reentrant subVIs.

 

There are cases where reentrancy is not absolutely necessary, for example for utility VIs that are called everywhere and that are fast to execute (e.g. several microseconds).

 

Also if you have a subVI that blocks execution for some time, but you don't want to have a clone for each call (to use less memory), using shared clones can be a good compromise.

 

 

Q2: An FGV (or action engine) is by definition global, so must be non-reentrant. Turning an FGV to reentrant clones (shared or pre-allocated) will cause you troubles.

 

With pre-allocated clones, your "FGV" will have one data space per instance of subVI on the diagram of the caller VI, multiplied by the number of instances of the caller VI itself. For example if you have 10 clones of your module, in which the "FGV" appears 10 times on the block diagram, there will be 100 instances of your "FGV".

 

With shared clones, a new clone will be created only when all previously allocated clones are currently being used. When a clone is not used anymore, it goes back to the pool and is available to use by another call. With the previous example with 10 modules and 10 calls on the diagram, the actual number of clones created for the "FGV" is not obvious at all and depends whether the calls are made simultaneously or not. It could go from 0 to 100 depending on the situation.

 


@Leah_E wrote:

I understand that VI's with uninitialised shift registers shouldn't be reentrant.


That is not necessarily true. Stateful VIs can also be reentrant (mostly pre-allocated). For example VI "Is Value Changed.vim" from the Comparison palette is one of those. It allows to retain data for the next time this node is executed on the diagram, but data is not shared between other instances of the node.

 

 

Q3: You can implement instance managers inside all your FGVs, but this may become a nightmare to maintain, because each FGV will essentially have the same duplicated code. My suggestion would be to get rid of the FGVs that must be "instanciated", wrap their data in clusters or classes and store it in a shift register in your module's main loop.

 

 

Q4: Clones have to be allocated at some point of your program, so the main thing you can adjust is when it is done. Using the "Populate Asynchronous Call Pool" at the initialization of the application can be a good strategy so that when you need to start the clones, it is done much quicker than if it is allocated "on-the-fly". Also If you use pre-allocated clones abusively on a very large set of subVIs, it could increase allocation time when creating a clone of your module, which is why I said earlier that simple/fast VIs do not necessarily need to be reentrant.

 

 

Hope this answers your questions.

 

Regards,

Raphaël.

Message 2 of 4
(416 Views)

Thank you so much for your explanations and suggestions Raphaël.

 

I'd totally forgotten that use case for Stateful VIs being reentrant, but that makes total sense!

 

Thanks so much suggesting refactoring to remove the FGVs and instead put the data into the module data cluster. This will make life much easier. I bulk-set all of the VIs except the main cloned VI back to non-reentrant and it's faster already. 😁 Shows it pays not to be 'too clever' with these things. Thanks to you I know what to do now!

0 Kudos
Message 3 of 4
(333 Views)

As I understand it, there is some overhead associated with reentrancy, so the tradeoff is not always obvious. Sometimes very simple VI's that execute very quickly perform better if they're NOT reentrant, as the overhead associated with launching the VI might take longer than just running the VI itself. It sounds like you're seeing that already.

 

You can also look at inlining your VI's if you're really trying to eek every last little bit of speed out of them. It takes more memory but usually that's not a problem.

0 Kudos
Message 4 of 4
(298 Views)