LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Howto call 'N' independent instances of "Decimate (continuous).vi" without placing 'N' instances of the VI on the block diagram?

Solved!
Go to solution
Highlighted

I have a 2D array of doubles that I would like to continuously decimate using the 'Decimate (continuous).vi'  located in the Signal Processing Pallet. This VI is set to preallocated clone reentrant as it uses a FGV to save state from call-to-call.  What I could do, but don't want to do, is have a huge index array and wire up 20+ 1D array of DBL to 20+ unique instances of the Decimate VI to make sure that each get their own dataspace and no "cross-talk" happens and then 'build array' them all back after the fact.

 

I'm nearly certain there is a much cleaner way to do this with just a single block diagram instance of the Decimate VI using call-by-reference techniques.   I found my way to this link: Preallocated-Reentrant-VI-within-Parallelized-For-Loop which talks about something similar.  After reading through all four pages and the detailed help for the 'Open VI Reference' function my head is still spinning about what option I want to pass (0x08 or 0x40 + 0x100) to guarentee that each time a 2D array of DBLs come in, each one is decimated using the same clone that was used the last time it was called.

 

Although the 2D array of DBL input always has the same number of rows now, it might not always be that number in the future and the ideal solution would not require me to create a static number of strictly typed references to the Decimate VI that will need to change as the number of rows in the 2D array of DBL grows.

 

Anybody willing to put together an example VI that takes an arbitrary 2D array of DBL as input, decimates each row using the same independant clone of the "Decimate (continuous).vi" and outputs the newly decimated 2D Array of DBL?  Assume every row uses the same decimation factor and 'Averaging' set to False.


0 Kudos
Message 1 of 8
(1,816 Views)
Highlighted
Solution
Accepted by topic author SeanDonner

Necessity is the mother of all invention and since it irks me when I read a post that has a similar problem with no resolution I felt obligated to post mine here.   I'm pretty sure this is best I can do within the confines of the current state of LabVIEW.   The one question I have is what happens if I set the Call-by-Reference For loop to be parallelizable?  Does that completely trash the 1-to-1 nature of what I'm aiming for?

 

DecimateContinuousByReference.png


0 Kudos
Message 2 of 8
(1,755 Views)
Highlighted

Hi, I had your original post queued up to try to answer but see that you've already moved forward.

 

When I was exploring this same thing (I'm in the thread you linked to), the code you presented looks just like what I settled on and have been using.  As to the question of parallelizing the Call-by-Ref loop, I have a nagging recollection of unexpected and undesired behavior.   As I recall, I had a tough time maintaining a 1-to-1 correspondence between the indices of the VI refnum array and either the iteration # i or the parallel instance #p of the For Loop as my reentrant processing function got called repeatedly in my app.

 

I don't have time now, but I'll try to follow up later b/c I think I put together some simple example test code to explore what I considered odd about the behavior of the i and p values in a parallelized for loop.  In my original app, I simply stopped pursuing parallelization b/c it was more a theoretical advantage than something I really needed.

 

 

-Kevin P

0 Kudos
Message 3 of 8
(1,717 Views)
Highlighted

I'm not 100% certain, but I'll wager a beer that the attached routine runs faster than yours, and is certainly simpler.  Indeed, it is your code, but without all of the (unnecessary) references.

 

You've created multiple reentrant clones, but you are calling them sequentially, so the second Decimate doesn't start until the first one finishes.  If you really want to have your N routines run "in parallel", then you need to run them Asynchronously, using the Start Asynchronous Call (and, since you want the results, you'll have to use Call and Collect, and patiently wait for all of them to finish and give you their results.

 

Should I lose this bet, I'll pay off at NI Week 2016.  Should I win, give me a kudo and drink the beer, yourself.

 

P.S. -- sorry, I don't have LabVIEW 2012 on this machine, but you should be able to recreate this Snippet from your earlier posted routine (which is precisely how I created it ...).

 

Non-reentrant Version.png

 

Bob Schor

0 Kudos
Message 4 of 8
(1,699 Views)
Highlighted

Hi Kevin. 


Appreciate the response.  Looking back at the thread I linked to, your post is exactly what led me down the right path which is why my solution looks highly derivitive of yours; so thank you for that!  My first try was using the Async Call & Collect framework with the pre-allocate clone pool method (options 0x40 || 0x100) but the test code I put together showed that the clone used was not always the one it used last time.

 

Using the CallByRef framework, on the other hand, gives me exactly the behavior I am looking for but I too mirror your sentiments that this envolves more code overhead than I would have liked.  It seems like this is a good candidate for some sort of syntactic sugar that could be added to a for loop that has an array of references on the input with a preallocated clone reentrant VI inside.

 

Out of curiosity, is there any way I can unqiuely identify which clone is being called using a VI Server method/property or must I alter the clone to take an input parameter that I pass in that is guarenteed to be unique?


0 Kudos
Message 5 of 8
(1,693 Views)
Highlighted

Bob_Schor wrote:

I'm not 100% certain, but I'll wager a beer that the attached routine runs faster than yours, and is certainly simpler.  Indeed, it is your code, but without all of the (unnecessary) references.


Hi Bob,

Indeed your code will likley run faster (so you'll win that bet) but the functionality is much different than mine.  Your code would work just fine If I used the single-shot varation of the Decimate vi, but I'm using the continuous variation which contains a number of uninitialized shift registers to maintain state from call-to-call.  Since your code only has one instance of the Decimate (continuous).vi on the block diagram, only a single clone is spawned and that same clone is used for each and every waveform in the 1D-array causing "cross-talk" between the waveforms and making the decimation nonsensical.


You've created multiple reentrant clones, but you are calling them sequentially, so the second Decimate doesn't start until the first one finishes.  If you really want to have your N routines run "in parallel", then you need to run them Asynchronously, using the Start Asynchronous Call (and, since you want the results, you'll have to use Call and Collect, and patiently wait for all of them to finish and give you their results.

 


It's not so much that I want them to run in parallel (i'm perfectly OK with them running sequentially), rather my requirement is to have each waveform in the array get their own clone of the Decimate (continuous).vi and each waveform uses the same exact clone it used the last time the for-loop runs.   Using the CallByRef method gets me both requriements, using the Async call-and-collect framework only gets me the first but it does not guarentee the second (that is there can still be cross-talk between the clones.)


0 Kudos
Message 6 of 8
(1,689 Views)
Highlighted

Nope, you win the bet.  I was wondering why you hadn't used the Reset terminal, designed for doing just what you describe (doing decimation "in pieces", but forgot that it is False by default, and while technically an error not to call it with "True" on the first call, the system will probably start with "nothing to see here" as its input, in which case you can get away with leaving Reset alone.

 

I think I might have been misled by the reference somewhere to parallel For loops ...  Plus I tend to write my own "Decimate Continuous" VIs that know how to handle 2D arrays all at once (it's not difficult) ...

 

Bob Schor

0 Kudos
Message 7 of 8
(1,676 Views)
Highlighted

Sean,

 

Some potentially good news -- I found my little example program and, based on front panel comments and a few verification runs, it looks like there may be a way to parallelize reliably.  Using the 'i' index to extract the VI refnum from the array seems to do the expected thing reliably.  I don't have any attempts to try auto-indexing in the example; once again a hazy memory is telling me that explicit indexing was found to maintain the needed 1-to-1 correspondence better than auto-indexing.  Can't vouch for that though.

   In the example, an array of integers where value=index is used to simulate the array of VI refnums.  Each time the parallelized For Loop runs, I check whether the order of values is the same as the last time the For Loop ran.  This simulates checking that the VI refnums are extracted in the same order every time, thus preventing crosstalk between reentrant instances (which retain important internal state data for continuous processing).

 

Second, there's a VI property called "CloneName" that will return a unique name for each clone instance.  I think it just appends an instance number to the name of the VI.

 

 

-Kevin P

 

test parallelized for loop behaviors.png

Message 8 of 8
(1,655 Views)