LabVIEW Idea Exchange

cancel
Showing results for 
Search instead for 
Did you mean: 
otto__

Eliminate uninitialized shift registers from vi.lib

Status: New

It's always a pleasure to uncover shared memory problems when working with base vi.lib methods that *should* work as preallocated clones and have uninitialized shift registers and then having to establish a chain of vi references to safely call those methods in an application with distributed/parallel threads. It feels like extra work but does fix the problem. It's a major unknown gotcha unless you're already familiar with the vi you are calling.

 

My most recent hiccup: the NI PID library. I needed two closed loop controllers in actor framework, and was getting very strange timing, setpoint, process variable, and control output crossovers until I realized the stock .vi's had shared memory in the form of uninitialized shift registers (despite being in the context of a pre-allocated actor). Creating references of the vi's I needed and storing them in the actor at launch fixed the problem, but at that point the effort in writing my own PID .vi starts to be a favorable time tradeoff. At least I am able to peek under the hood of the PID library, other aspects of vi.lib... not so much.

 

Or maybe this is a teaching problem. I haven't come across ways of navigating this issue from official NI documentation, in fact the way I learned I needed to call the PID.vi by-reference was from the forum and rather matter-of-factly. There are a couple of great blogs that cover this issue in detail, so I don't feel alone in my ignorance. Maintaining State Information in LabVIEW Applications, Part 5 - LabVIEW Field Journal Archives | Lab...

8 Comments
wiebe@CARYA
Knight of NI

I don't really object to the PtByPt paradigm.

 

I do share your frustration. The IP is there, but it's useless.

 

I agree that they don't scale up and, more importantly, that this problem is not clear. I explained this problem quite a few times (incl. to NI) and never caught anybody listening.

 

Another objection is that PtByPt is not "dataflow", which is actually why you (and I) can't use them OoTB. They are shortcuts and we pay the price. 

 

Of course using references works, but references are yet another step down on the dataflow scale.

 

But I don't think removing those PtByPt VIs is a solution; they are used successfully by many people (mostly  non-OO people I presume). When a simply PtByPt VI suffices, replacing it with a stateless VI would be quite cumbersome (adding wires, an init and at least one shift register).

 

IMO, A better solution would be to give us the same libraries but with state in (private data) input and outputs. Ideally, to prevent duplicate code, the existing code would be refactored to wrap the new code, keeping the data in a shift register and calling the stateless VI..

UliB
Active Participant

I have to admit: I do not understand the problem. But according to the idea, wiebes comment and the Kudos for wiebes message I see that there has to be a problem.

Why do uninitialized shift registers in VIs, which are configured to preallocate clones, act like shared memory? (Is this the problem at all?)

I understand this in non-reentrant VIs and I understand why and how it can happen in VIs configured to share clones.

The link to "Maintaining State Information in LabVIEW Applications" doesn't help me to understand the problem either, because it states "To let this VI use shared clones (and thus use less memory if this VI is called a lot), we need to move the state information out of this VI."

Can someone teach me or give me an example?

Intaris
Proven Zealot

I don't see anyone claiming a VI set as preallocated clone with a shift register results in shared data.....

 

All I see is otto saying that vi.lib VIs that SHOULD work as preallocated clones. You know, the actual VIs NI exposes.

 

Of course, if a preallocated clone calls a non-reentrant sub-VI which itself has internal memory, the preallocated portion of the code in the parent VI is irrelevant. A preallocated clone does not mean that all of its sub-VIs are also preallocated clones. Somewhere in the hierarchy there is a VI call which is not compatible. Or a DLL call or something.

UliB
Active Participant

Thank you. OK, I see a difference between


@otto__ wrote:

[...] vi.lib methods that *should* work as preallocated clones and have uninitialized shift registers [...]


and what I thought the problem was.

 

How did I get to this question?


@otto__ wrote:

My most recent hiccup: the NI PID library. [..] until I realized the stock .vi's had shared memory in the form of uninitialized shift registers [..]


I looked at "Control & Simulation" -> "PID" palette (Is this the right one?). I saw 15 VIs in the palette and opened them. In my installation 13 were preallocate clones, some have uninitialized shift registers, and two were non-reentrant, without any shift registers at all. (I did not check the VI hierarchy of those 15 VIs.)

I asked, because I did not see the problem in this PID library, but I can imagine that there are these kind of problems in vi.lib.

wiebe@CARYA
Knight of NI

Here's a simple demonstration of the problem.

 

It's really not complicated at all.

 

1) There's a Class A with a method B that does something.

2) Method B calls "Value Has Changed PtByPt.vi" (anything with state).

3) Method B is called and it  works like a charm.

 

Now we're scaling up...

 

4) We're making an array of class A objects.

5) We're calling the method B on each object in a for loop.

6) We're screwed...

 

It doesn't matter at all if we're using reentrancy or which kind. The method itself shouldn't need to be reentrant at all and the PtByPt VI obviously shouldn't be either. It won't matter anyway as method B is called in a for loop and it's one and the same instance that's being called for each iteration.

 

The example is in fact doing exactly this:

wiebeCARYA_1-1752481166571.png

"Value Has Changed PtByPt.vi" does what it's supposed to do, but the result is not what we'd like.

 

The internal state is simply useless for this purpose. We need state by wire:

wiebeCARYA_4-1752481420744.png

 

UliB
Active Participant

Thank you wiebe for the explanation. This really clears things up.

otto__
Member

And if my language was too strong (*eliminate*), I would also be okay with something more suggestive such as an "open", "create", "initialize" .vi call that was required for those point-by-point type .vis (along with a "close", "destroy", etc.), which essentially turns them into what wiebe put in the example... it needs a "stateful" wire to declare whether it has been properly initialized and to do the (existing) shift register propagation from one call to the next without causing problems when you want to use that .vi/tool in a parallel and asynchronous fashion (or iterating over a group of items that need a pt by pt operation in wiebe's example). With some of the pt by pt .vi's I get the notion that I should treat the inputs/outputs of each one as a separate item if I am working in one big 'ol monolithic .vi, but when I am doing stuff in a services-oriented application, its real easy to make calls to the same built-in .vi (PID or pt-by-pt or something else I don't know about) in different parts of the application without knowing about uninitialized shift registers and the very weird problems they can create.

wiebe@CARYA
Knight of NI

In that case, +1 for me...