Jump to content

Buffer Allocation: Difference between revisions

From LabVIEW Wiki
No edit summary
Rewrite and extend
 
Line 1: Line 1:
Note:  The following was copied from a [http://forums.lavag.org/Another-reason-why-copy-dots-is-a-bad-name-for-buffer-allocations-t10406.html#entry43277|forum post] by Aristos Queue.
LabVIEW uses various techniques<ref name="labview_compiler">[http://www.ni.com/tutorial/11472/en/ LabVIEW Compiler: Under the Hood] (National Instruments Tutorials)</ref> to optimize code as much as possible. One of those features is the optimization of buffer allocations, which avoids making copies of data in memory if they can be avoided. This is especially useful for large datasets like arrays and clusters, which - by nature - have a high memory footprint. However, sometimes code gets too complex or is written in a way that makes it hard for the compiler to perform optimizations<ref>[http://zone.ni.com/reference/en-XX/help/371361R-01/lvhowto/compiler_limiting_optimizations/ Choosing between Editor Responsiveness and VI Execution Speed] (LabVIEW 2018 Help)</ref><ref>[https://forums.ni.com/t5/LabVIEW/Understanding-code-complexity/m-p/2462456?profile.language=en#M755053 Understanding code complexity] (National Instruments Forums)</ref>. For those cases, some optimizations are skipped and less efficient code gets compiled. This can result in data being copied in the final application and LabVIEW running "out of memory".


There is a tool in LabVIEW called the "Show Buffer Allocations" tool. You can find it in a VI's menu at Tools>>Profile>>Show Buffer Allocations.... It is a useful tool for doing memory optimizations because it shows little dots every place that the LabVIEW compiler made a buffer allocation. Some people call these "copy dots" because people *think* these indicate where LabVIEW is making a copy of the data. About 90% of the time, that is accurate. But there are some cases where a "buffer allocation" and a "copy" are not the same thing at all. Today I want to mention one that I haven't posted before.  
A programmer that is aware of such limitations can write their code accordingly, which is why LabVIEW provides an utility to visualize buffer allocations in the code.


When using arrays, one of the major times when buffer allocations are of interest to programmers, not all buffer allocations are copy dots. Whenever possible, LabVIEW will do an operation on an array by creating a "subarray". If you pay close attention to the Context Help window, you'll sometimes see wires that are of "subarray" type. A subarray is simply a reference to another array, storing an index into that array and a stride and a length. This is a very efficient way to do some pretty complex operations without actually making new arrays, such as decimate array, split array, and reverse array. That last one returns an index to the last element of the array and a stride length of -1. The return of a subarray is only possible when LabVIEW knows that no other operation is going to be modifying the value of the array, so it is safe for the reference to the array in memory to be shared.
== Show buffer allocations ==


Now, take a look at this diagram. Notice the buffer allocations:<br />
[[File:Buffer Allocation - Show Buffer Allocations Dialog.png|thumb|Show Buffer Allocations dialog]]
[[Image:subarraycopydots.png]]  


The "Split 1D Array" node has two output buffer allocations. A lot of people would think, "Look at those copy dots. That means LabVIEW is making two new arrays, one for the first part of the array, and one for the second part of the array." Not true. The buffer allocations are *for the subarrays*. Remember I said that a subarray records a reference to the array, a starting index, a length and a stride. Those four items of data have to be stored somewhere. The buffer allocation is the allocation to store those four items. It is not a copy of the entire array. The output of the Build Array, on the other hand, is a full array allocation.  
Since [[LabVIEW 7 Express|LabVIEW 7.1]], there is a built-in utility that visualizes buffer allocations on the block diagram. When enabled, black dots indicate buffer allocations. These can be shown for specific or all types. By default only ''Arrays'' and ''Clusters'' are shown, because those two types typically have the greatest impact on memory consumption.


To see what is being allocated at any given buffer allocation, look at the type of the wire.  
{| class="wikitable"
! Type
! Description
|-
| [[File:Checkbox Checked.png]] Arrays
| Shows buffer allocations for [[Array|arrays]].
|-
| [[File:Checkbox Checked.png]] Clusters
| Shows buffer allocations for [[Cluster|clusters]].
|-
| [[File:Checkbox Unchecked.png]] Strings
| Shows buffer allocations for [[String|strings]].
|-
| [[File:Checkbox Unchecked.png]] Paths
| Shows buffer allocations for [[Path|paths]].
|-
| [[File:Checkbox Unchecked.png]] Scalars
| Shows buffer allocations for [[Scalar|scalars]].
|-
| [[File:Checkbox Unchecked.png]] Variants
| Shows buffer allocations for [[Variant|variants]].
|-
| [[File:Checkbox Unchecked.png]] All other types
| Shows buffer allocations for any other type not mentioned above.
|-
| [[File:Checkbox Unchecked.png]] Dynamic dispatch temporaries
| Shows ''temporary'' buffer allocations for [[Dynamic dispatch|dynamic dispatch]] VIs. These buffer allocations are ''temporary'' because a buffer allocation might or might not happen depending on the descendant.
|}


And don't call them "copy dots." :-)
<br clear="all">
== Uncritical buffer allocations ==


== External Links ==
Buffer allocations are shown for the code that was written by the programmer, not for the code after it was optimized by the compiler. This means that some buffer allocations are not as critical as they appear to be at first glance.
* [http://zone.ni.com/reference/en-XX/help/371361D-01/lvconcepts/vi_memory_usage/ LabVIEW 8.5 Help>>VI Memory Usage]


[[category:memory management]]
=== Constants and constant expressions inside loops ===
 
Constants and constant expressions inside loops are initialized once and re-used for each iteration. The buffer allocation dot only indicates that the value in-fact requires a buffer being allocated for it. It doesn't mean that the buffer is re-allocated on every iteration of the loop. The compiler will instead "move" the code outside the loop. This process is called [[Loop-Invariant Code Motion]]<ref name="labview_compiler" />.
 
{|
| [[File:Loop-Invariant Code Motion - Before.png|frame|Loop-Invariant Code Motion before]]
| [[File:Loop-Invariant Code Motion - After.png|frame|Loop-Invariant Code Motion after]]
|}
 
=== Indexing tunnels on loops ===
 
Buffer allocations for indexing tunnels of for and while loops behave differently.
 
For loops execute code a specific number of times. This means that the maximum buffer size can be determined by multiplying the number of iterations with the size of one element. While loops, on the other hand, execute an arbitrary number of times which means that the buffer is allocated once and re-sized dynamically if more space is required<ref>[https://forums.ni.com/t5/LabVIEW/memory-benefits-using-conditional-For-Loop/m-p/642755#M296925 Memory benefits using conditional For Loop] (National Instruments Forums)</ref>.
 
[[File:Buffer Allocation - Indexing Tunnels.png]]
 
=== Controls ===
 
Controls are displayed as buffer allocations, which isn't necessarily true if the VI is used as a subVI. Depending on the code, values may even pass through a VI without any buffer allocations. A very common example for this are the error in/out terminals.
 
[[File:Buffer Allocation - Controls.png]]
 
== Controversy: "Copy dots" vs. "Buffer allocations" ==
 
The following section is based on a post by [[User:Smercer|Aristos Queue]] on [[LAVA]]<ref>[https://lavag.org/topic/7307-another-reason-why-copy-dots-is-a-bad-name-for-buffer-allocations/?do=findComment&comment=42524 Another reason why "copy dots" is a bad name for "buffer allocations"] (LAVA Topic)</ref>.
 
Some people call buffer allocations "copy dots" because they believe these indicate where LabVIEW is making a copy of the data. About 90% of the time, that is accurate. But there are some cases where a "buffer allocation" and a "copy" are not the same thing at all.
 
When using arrays, one of the major times when buffer allocations are of interest to programmers, not all buffer allocations are copy dots. Whenever possible, LabVIEW will do an operation on an array by creating a "subarray". In the [[Context Help window]] these wires are displayed as "subarray" type.
 
[[File:Buffer Allocation - Subarray Type.png]]
 
A subarray is simply a reference to another array, storing an ''index'' into that array and a ''stride'' and a ''length''. This is a very efficient way to do some pretty complex operations without actually allocating new arrays, such as [[Functions Palette/Programming/Array#Decimate Array|Decimate Array]], [[Functions Palette/Programming/Array#Split Array|Split Array]], and [[Functions Palette/Programming/Array#Reverse Array|Reverse Array]]. In fact, ''Reverse Array'' returns an index to the last element of the array and a stride length of -1.
 
The return of a subarray is only possible when LabVIEW is certain that no other operation will modify the value of the array, so it is safe for the reference to the array in memory to be shared.
 
Notice the buffer allocations in the following diagram:
 
[[File:Buffer Allocation - Black Dots Indicate Buffer Allocations.png]]
 
The [[Functions Palette/Programming/Array#Split 1D Array|Split 1D Array]] node has two output buffer allocations. These buffer allocations are for the ''subarrays'', which record a reference to the array, a starting index, a length and a stride. Those four items have to be stored somewhere. The buffer allocations are for those four items. It is not a copy of the entire array.
 
The output of the [[Functions Palette/Programming/Array#Build Array|Build Array]], on the other hand, is a full array allocation.
 
To see what kind of array is being allocated, look at the type of the wire using the [[Context Help window]].
 
== Best practice ==
 
* Use the ''Show Buffer Allocations'' dialog to analyze buffer allocations of memory-critical code.
* Add elements to the end of arrays, not at the front.
* Use the [[Functions Palette/Programming/Array#Reverse Array|Reverse Array]] function to add elements at the front of arrays.
* Don't optimize buffer allocations for subarrays.
 
== History ==
 
{{ambox|text=Which version added the "Dynamic dispatch temporaries" option? 8.6 didn't have it, 2015 has it.}}
 
{| class="wikitable"
! Version
! Change(s)
|-
| [[File:LV8-2013.png|frameless|border|64x64px|LabVIEW 8|link=LabVIEW 8]]
| Moved the utility to ''Tools >> Profile >> Show Buffer Allocations...''
|-
| [[File:LV7.png|frameless|border|64x64px|LabVIEW 7|link=LabVIEW 7 Express]]
| Added the ability to show where buffer allocations occur in the code (''Tools >> Advanced'')
|}
 
== See also ==
 
* [[Profile Buffer Allocations]]
* [[Functions Palette/Programming/Structures/In Place Element Structure|In Place Element Structure]]
* [[Functions Palette/Programming/Application Control/Memory Control|Memory Control Palette]]
 
== External links ==
 
* [https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z000000PAcBSAW How does LabVIEW allocate a new buffer?] (National Instruments Knowledge Base Article)
* [https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z000000P9mtSAC Memory Is Full Error in LabVIEW] (National Instruments Knowledge Base Article)
 
== References ==
<references />
 
[[Category:Memory management]]

Latest revision as of 09:50, 30 August 2019

LabVIEW uses various techniques[1] to optimize code as much as possible. One of those features is the optimization of buffer allocations, which avoids making copies of data in memory if they can be avoided. This is especially useful for large datasets like arrays and clusters, which - by nature - have a high memory footprint. However, sometimes code gets too complex or is written in a way that makes it hard for the compiler to perform optimizations[2][3]. For those cases, some optimizations are skipped and less efficient code gets compiled. This can result in data being copied in the final application and LabVIEW running "out of memory".

A programmer that is aware of such limitations can write their code accordingly, which is why LabVIEW provides an utility to visualize buffer allocations in the code.

Show buffer allocations

Show Buffer Allocations dialog

Since LabVIEW 7.1, there is a built-in utility that visualizes buffer allocations on the block diagram. When enabled, black dots indicate buffer allocations. These can be shown for specific or all types. By default only Arrays and Clusters are shown, because those two types typically have the greatest impact on memory consumption.

Type Description
Arrays Shows buffer allocations for arrays.
Clusters Shows buffer allocations for clusters.
Strings Shows buffer allocations for strings.
Paths Shows buffer allocations for paths.
Scalars Shows buffer allocations for scalars.
Variants Shows buffer allocations for variants.
All other types Shows buffer allocations for any other type not mentioned above.
Dynamic dispatch temporaries Shows temporary buffer allocations for dynamic dispatch VIs. These buffer allocations are temporary because a buffer allocation might or might not happen depending on the descendant.


Uncritical buffer allocations

Buffer allocations are shown for the code that was written by the programmer, not for the code after it was optimized by the compiler. This means that some buffer allocations are not as critical as they appear to be at first glance.

Constants and constant expressions inside loops

Constants and constant expressions inside loops are initialized once and re-used for each iteration. The buffer allocation dot only indicates that the value in-fact requires a buffer being allocated for it. It doesn't mean that the buffer is re-allocated on every iteration of the loop. The compiler will instead "move" the code outside the loop. This process is called Loop-Invariant Code Motion[1].

Loop-Invariant Code Motion before
Loop-Invariant Code Motion after

Indexing tunnels on loops

Buffer allocations for indexing tunnels of for and while loops behave differently.

For loops execute code a specific number of times. This means that the maximum buffer size can be determined by multiplying the number of iterations with the size of one element. While loops, on the other hand, execute an arbitrary number of times which means that the buffer is allocated once and re-sized dynamically if more space is required[4].

Controls

Controls are displayed as buffer allocations, which isn't necessarily true if the VI is used as a subVI. Depending on the code, values may even pass through a VI without any buffer allocations. A very common example for this are the error in/out terminals.

Controversy: "Copy dots" vs. "Buffer allocations"

The following section is based on a post by Aristos Queue on LAVA[5].

Some people call buffer allocations "copy dots" because they believe these indicate where LabVIEW is making a copy of the data. About 90% of the time, that is accurate. But there are some cases where a "buffer allocation" and a "copy" are not the same thing at all.

When using arrays, one of the major times when buffer allocations are of interest to programmers, not all buffer allocations are copy dots. Whenever possible, LabVIEW will do an operation on an array by creating a "subarray". In the Context Help window these wires are displayed as "subarray" type.

A subarray is simply a reference to another array, storing an index into that array and a stride and a length. This is a very efficient way to do some pretty complex operations without actually allocating new arrays, such as Decimate Array, Split Array, and Reverse Array. In fact, Reverse Array returns an index to the last element of the array and a stride length of -1.

The return of a subarray is only possible when LabVIEW is certain that no other operation will modify the value of the array, so it is safe for the reference to the array in memory to be shared.

Notice the buffer allocations in the following diagram:

The Split 1D Array node has two output buffer allocations. These buffer allocations are for the subarrays, which record a reference to the array, a starting index, a length and a stride. Those four items have to be stored somewhere. The buffer allocations are for those four items. It is not a copy of the entire array.

The output of the Build Array, on the other hand, is a full array allocation.

To see what kind of array is being allocated, look at the type of the wire using the Context Help window.

Best practice

  • Use the Show Buffer Allocations dialog to analyze buffer allocations of memory-critical code.
  • Add elements to the end of arrays, not at the front.
  • Use the Reverse Array function to add elements at the front of arrays.
  • Don't optimize buffer allocations for subarrays.

History

Version Change(s)
LabVIEW 8 Moved the utility to Tools >> Profile >> Show Buffer Allocations...
LabVIEW 7 Added the ability to show where buffer allocations occur in the code (Tools >> Advanced)

See also

External links

References