01-07-2009 11:36 AM - edited 01-07-2009 11:38 AM
Hello all, I am hoping I will be able to find some help with some questions that I have. I am a relatively new labVIEW programmer, I have been writing data acquisition and data analyis applications for over a year now. I wrote a data analyis program that took quite some time to code but I never thought about the need to be memory conscious. Sure enough, the application worked under "standard operation", but when this application was pushed to the limit by loading in large amounts of data I found the application crashing due to not enough memory. This prompted me to begin researching about memory efficient techniques for labVIEW programming. I discovered some of the easier techniques to implement such as not using build array functions and other functions that resize the data because data buffers cannot be reused. Just today I discovered some techniques that seem to be very powerful and perfect for the type of application that I am rebuilding. I have some confusion about them though. The two techniques that I came across are utilizing the Queue functions and also the Functional Global technique. Both seem to offer similar benefits but I am not sure which method to choose. Answers to the questions that follow will help me to determine which technique to use.
1. When using the Dequeue Element function is a copy being made of the data that is being accessed that has been stored in the Queue? When I created a VI that first created the Queue with the Obtain Queue function and then tried to access the data using the Dequeue Element function, using the show buffer allocation tool it showed that a buffer was being allocated where the data was being accessed from the Dequeue Element function. View the Dequeue Element.png file attached to see this. I thought that using queues was suppose to elimitate copies of data. (BTW I was having difficulty putting pictures in the post, is there an easy wasy to do this?)
2. During my research of memory efficient programming I came acorss a page on NI.com that was very helpful <http://zone.ni.com/devzone/cda/tut/p/id/3625>. The information on this page was very useful but when I inspected some of the VI's listed on the page it brought up more questions. Within the giga_labview.llb there is a VI called GLV_GigaLabVIEWMemoryStoreAndBrowse.vi. This VI uses the functional global technique to store/modify/read data. I opened the block diagram to this VI an then opened the Show Buffer Allocation VI and to my suprize every ouput on the call by reference node while calling the functional global VI had a buffer allocated. See Functional Global.png file attached for a picture of this. When a VI is called, and some of the outputs are not used, are buffers still created for the data at the unused outputs? The Show Buffer Allocation tool shows that buffers are created whether the data at the outputs is used or not.
Besides the above two questions I am also looking for some guidance on whether to use the Queue or Functinal Global method. Is there any information that shows the pros and cons of the two different techniques and possibly compares and contrasts them?
Thank you very much for any help with these questions, your time if greatly appreciated.
01-07-2009 12:11 PM
Why am I a sucker for these types of questions. ![]()
We (the contributors to this forum) have been known to go on for days analyzing a single wire and you are asking about a whole pile of them.
Re: #1
If moving data from one place to another the queues "kick-arse" and will transfer data using a pointer to the data that is queued up, IF POSSIBLE. In your example

The data originates in a control. If the data was "transfered" using a pointer, modifications would take place in the data buffer that holds the data in the control. So to work in the original buffer would result in the data in the control changing. Effectively the data would be flowing backwards through the wire. On the next run, the modified data buffer would be used. So LabVIEW's In-placeness algorithm recognizes this construct and creates a new buffer so you can do your crunching and the data in the control is un-touched.
That was the easy part of your question.
Please review the Link and related links in this tag for discusion of performance issues in LV. In that collection you should find the "Clear-as-mud" thread. A careful study of that thread will show you that provided a sub-VI is of the proper form (see that thread) data is passed into and returned from sub-VI as pointers that point at the buffer in the caller. So to comment on all of those buffer allocations you are seing

I would have to study the code used in that VI.
Rules of Thumb*
If your data all end up at one place, then go with a queue.
If you can do the same thing with a queue or an Action Engine, use the queue
If a queue does not meet the needs of your app, develop an AE that does.
For more words on Action Engines (FGV's) you may want to take a look at my Nugget on Action Engines.
I hope that helps,
Ben
* please point out if these rules are not valid!
01-07-2009 12:53 PM - edited 01-07-2009 12:53 PM
Have you tried the functional global approach, but using subVIs rather than call-by-reference? Out of curiousity, why are you using call-by-reference here, if your VI name is a constant (and if you do need call-by-reference with a constant name, why not use a static VI reference)? With a subVI, I suspect LabVIEW can take advantage of reusing inputs as outputs, which is not possible for call-by-reference since LabVIEW doesn't know anything about how a by-reference call will use memory ahead of time.
As for how to insert pictures into your posts, upload the image as an attachment. Copy the location of the link to your image, then edit your post, click the insert/edit image button, and paste the location of your attachment.
01-07-2009 12:57 PM
It seems that if you create a copy of the entire data that has been queued up when you want to access only a small portion of it then functional globals seem to be better because you can set up the database VI that stores the data to output pieces of the data if that is all you are trying to access. When trying to access a subset of data being stored by using the functinal globals it seems that a new buffer will be allocated that could be a fraction of the size (if programmed correctly) of the buffer that will be allocated when using the Dequeue Element function. With the Dequeue Element function there is no option to access only a subset of the data. Any input is greatly appreciated. Thank you.
01-07-2009 01:05 PM
maggs145 wrote:It seems that if you create a copy of the entire data that has been queued up when you want to access only a small portion of it then functional globals seem to be better because you can set up the database VI that stores the data to output pieces of the data if that is all you are trying to access. When trying to access a subset of data being stored by using the functinal globals it seems that a new buffer will be allocated that could be a fraction of the size (if programmed correctly) of the buffer that will be allocated when using the Dequeue Element function. With the Dequeue Element function there is no option to access only a subset of the data. Any input is greatly appreciated. Thank you.
Correct!
nathand wrote:
...I suspect LabVIEW can take advantage of reusing inputs as outputs, which is not possible for call-by-reference since LabVIEW doesn't know anything about how a by-reference call will use memory ahead of time...
I believe*...
The call by reference node requires the specification of the VI's icon which is similar to a function protoype in other languages. There is enough info in the icon connector to ID what values can be passed by reference and which ones must be copied.
Ben
* Since I am not NI I am not privy to that detail.
01-07-2009 01:05 PM
I found the call-by-reference example that is posted on NI.com and was just an example, I havent used this in my code yet. What exactly do you mean by "LabVIEW can take advantage of reusing inputs as outputs." If an input is the same exact type and size as an output, LabVIEW will use the same buffer?
Thanks for all your help.
01-07-2009 01:09 PM
maggs145 wrote:I found the call-by-reference example that is posted on NI.com and was just an example, I havent used this in my code yet. What exactly do you mean by "LabVIEW can take advantage of reusing inputs as outputs." If an input is the same exact type and size as an output, LabVIEW will use the same buffer?
Thanks for all your help.
Please read the "Clear as Mud" thread.
Greg McKaskle (LV R&D) came out of retirement to try and help clarify that Q. Your statement is mostly there but here are other conciderations to take into account before I can say yes.
Ex:
Is buffer modified by caller?
Are inputs and outputs on the root of the diagram?
All of these req's are mentioned in the "Clear as Mud" thread.
Ben
01-07-2009 01:28 PM
01-07-2009 01:37 PM - edited 01-07-2009 01:38 PM
maggs145 wrote:
Thanks for all your help Ben. Just to be sure, is the thread that you are talking about entitled " case structure parameter efficiency?" I cannot find a thread entitled "Clear as Mud"
Yes that is the one! I can never remeber the real name but I can remember Greg's closing query which is a unique search string and is easy for me to find. Again read Greg's posting carefully. Greg was one of my earliest LabVIEW mentors and knows more about LV and memory management than any other person that I can think of off-hand.
Just trying to help,
Ben
01-07-2009 01:46 PM
Ben wrote:
nathand wrote:
...I suspect LabVIEW can take advantage of reusing inputs as outputs, which is not possible for call-by-reference since LabVIEW doesn't know anything about how a by-reference call will use memory ahead of time...
I believe*...
The call by reference node requires the specification of the VI's icon which is similar to a function protoype in other languages. There is enough info in the icon connector to ID what values can be passed by reference and which ones must be copied.
Ben
* Since I am not NI I am not privy to that detail.
This cannot be the case, as I think you speculated in this thread on LAVA (see second page). That discussion looked at whether required terminals provide better performance than recommended ones, and concluded that there is a (small) performance gain from using required terminals. However, call-by-reference doesn't know if terminals are required or recommended and can't take advantage of this optimization.
The same logic applies for two VIs with identical connectors but different in-place behavior. You can see this by creating a simple test VI that calls a subVI with a single input and a single output connected to each other - no buffer is allocated. Now modify the subVI in a way that prevents buffer reuse (a case structure is often good for this, to create two possible sources for the output). When you save the subVI you'll notice that the caller VI is marked as modified because its memory allocation needs have changed due to the subVI. Now do the same thing but call the subVI through call-by-reference - as long as you don't change the subVI's connector pane the calling VI won't be modified, even if you change the way buffers are reused. This demonstrates that call-by-reference can't take advantage of in-place performance the way a subVI call can, and that the connector pane alone does not provide enough information to determine if a buffer can be reused.