LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Asynchronous call memory leak on LV2019

I'm having problems in upgrading an application from LV2015 sp1 to LV2019 sp1. I detected memory leak after running the executable recompiled by new version of LabVIEW.  After some debugging I found that this problem is related to the asynchronous call VI. Calling the VI several times without closing the reference to it seems to behave differently in each version of LabVIEW. The figure below shows the minimal code needed to reproduce this issue. The test.vi just makes a simple addition operation and it is called with call and forget flag enabled.

 

test.png

 

 

I tested this code on both versions of LabVIEW. The 2015 version is installed on a Windows Server 2008 R2 and the 2019 version on a Windows Server 2012. The handle count curve looks very similar on both version of LabVIEW. However, the result of memory allocation is totally different. On 2015 version the memory allocation seems to happen by chucks of data, steadying after some time. While in 2019 this process happens more smoothly but keeps increasing the memory allocation indefinitely.

 

Performance LabVIEW 2019Performance LabVIEW 2019Performance LabVIEW 2015Performance LabVIEW 2015

 

 

As a workaround I could open and close the VI reference every time it is called, but this could increase latency on the application execution.

 

Is this behavior expected from LabVIEW 2019?

0 Kudos
Message 1 of 4
(2,292 Views)

You are calling your Test VI using reentrant clones "as fast as you can".  Think about what your While Loop is doing:

  • It creates a Clone of Test and starts it running asynchronously.
  • It then loops, and does this again.  If you are using Shared Clones, it tries to find a Clone that has "finished" to reuse it, otherwise it allocates memory for another Clone.
  • The speed at which this code runs will depend critically on how memory is allocated when Clones are created, and how it is managed and returned to the free memory pool.  It would not at all surprise me if this didn't change from one Version of LabVIEW to another.

So I'm going to wear my Professor Hat again, just to say a few more words on Asynchronous Clones and LabVIEW Re-entrant VIs..

 

I've used Asynchronous Clones in a number of applications, but I don't think I've ever just let them keep "multiplying" (that is, have the number of Clones be essentially unbounded).  My Clones fall into two categories:

  • I want to run the same basic algorithm on five identical hardware setups (with the hardware having, for example, different VISA Port numbers to distinguish them).  My routine takes the VISA Port as an input, so if it is called 5 times as an Asynchronous Clone, each with a different VISA Port, they will all run "in parallel" until they finish.  In any case, the number of Clones is "hardware-bounded".
  • The other Clone category is when coding a recursive algorithm (for example, walking a directory tree).  Here there is no fixed bound, but for most well-posed situations, it will be finite.  An easy example is to ask "How many Files are there in this Folder, including files in sub-Folder?".

I rather like Recursive functions, and the "Files in Folder" VI described above is a nice example to explore.  I'm not going to post the VI, but I will tell you how to write it (if you are interested), hoping the exercise of "doing it yourself" will be fun (and educational).

 

Start with a Standard LabVIEW VI using the 4-2-2-4 Connector Pane, with Error In and Error Out on the lower corners, a Path Control called Folder Path in upper Left, and an I32 Numeric Indicator called Files in Folder in the Upper Right.  Once you have this much, double-click the generic NI Icon next to the Connector Pane and Icon Editor.  Click the Layers Tab, click the NI Icon on User Layers and delete it (red X to the right).  Now choose the "Blue Square" (a.k.a. Filled Rectangle) Tool on the right of the Editor, and Click-Drag from Upper Right to Lower Left to make a White Square with Black Border.  Now choose the Icon Text tab, and write Files in Folder (one word on each line looks best, to me -- I also use 10 point type and turn off Capitalize Text).  The reason for making the Icon will become very important in a few steps.  Now click "OK" (underneath the Icon you just made) to save your VI's new Icon.

 

Before starting to code, save your VI (which consists of a nice Icon, but little else) as "Files in Folder.vi".  Open it, right-click its Icon in the upper-right corner, and choose VI Properties, Execution.  Select "Shared Clone Execution".  Now we are ready to write the code.

 

Wire the List Folder function to Error In and Folder Path.  This function returns two String Arrays, a list of Files and a list of (sub-)Folder it finds in the Folder.  By taking the size of the "Files" Array, we can get the number of files in this particular Folder.  But what can we do with the list of Folders?  Why, we can combine them with the Folder Path to get a new "Folder Path" to each sub-Folder, and pass this Folder to the function ... "Files in Folder", which just happens to be the function we are writing!  [Incidentally, this is the reason for creating the Icon and saving the Function the the previous paragraph.

 

[I'd written a few paragraphs here, then when I tried to Post, the Forum glitched and I lost most of it.  So I'm going to give the Abbreviated Version here.  You want to create a For Loop, into which you put the Files in Folder VI we are currently writing (this is the "magic" of Recursion, and why we created and saved this VI, with Icon).  Bring in the Folder Path and the Array of Folder Names (which will come in through an Indexing Tunnel).  Do a Build Path (inside the For Loop) to create the full Path to the sub-Folder, wire it to Files in (sub-)Folder, and bring Files in Folder out through another Indexing Tunnel.  Sum this Array, add the count of the number of Files from List Folder, and we are done!  Magic!!

 

Test it.  You will get one of three types of results.  If you run it on one of your (own) "ordinary" Folders, with no hidden files or folders, you should get the same File Count that you would get if you asked Window's File Explorer for the Properties of the Folder.  If there are hidden files (for example, if it includes a Subversion Working Copy), it will "undercount", as LabVIEW's function doesn't seem to count Hidden Files.  Finally, if you are foolish enough to ask it about C:\, you'll get Error 8 (left as an exercise for the Reader).

 

Bob Schor

 

0 Kudos
Message 2 of 4
(2,242 Views)

@Bob_Schor wrote:
  • It creates a Clone of Test and starts it running asynchronously.
  • It then loops, and does this again.  If you are using Shared Clones, it tries to find a Clone that has "finished" to reuse it, otherwise it allocates memory for another Clone.

I think (I avoid asynch calls*) the code opens one reference to one new clone, and then call this clone multiple times. There's only one reference, and that should result in only one clone. This clone should only be able to execute once, since there is only one top level data space for it. The call by reference should call the clone and the next iteration should wait for it to finish. Place an imaginary question mark behind all these sentences...


Does it matter which VI you are running? Can we exclude a leak in the VI?

 

I'll do some testing myself as well.

 

* This is a text book case for OO Polymorphism...

0 Kudos
Message 3 of 4
(2,222 Views)

wiebe@CARYA wrote:

@Bob_Schor wrote:
  • It creates a Clone of Test and starts it running asynchronously.
  • It then loops, and does this again.  If you are using Shared Clones, it tries to find a Clone that has "finished" to reuse it, otherwise it allocates memory for another Clone.

I think (I avoid asynch calls*) the code opens one reference to one new clone, and then call this clone multiple times. There's only one reference, and that should result in only one clone. This clone should only be able to execute once, since there is only one top level data space for it. The call by reference should call the clone and the next iteration should wait for it to finish. Place an imaginary question mark behind all these sentences...


OK, that does not work as expected at all.

 

The Start Asynchronous Call creates new clone references by itself.

 

So, Bob, you are right about what is happening.

 

In LV18, I didn't see a memory leak though.

 

So for OP, what is the desired behavior? What are you actually trying to do?

0 Kudos
Message 4 of 4
(2,215 Views)