LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

buffer allocation and minimizing memory allocation

Solved!
Go to solution

Hi,

 

I'm tryint to minimize buffer allocation and memory activity in general. The code will be running "headless" on a cRIO and our experience and that of the industry at large is to ellliminate or minimize any dynamic memory allocatio and deallocation actions. 

 

In our case, we are unfortunately dealing with a lot of string manipulations, so eliminating all memmory alloc/dealloc is non-trivial (impossible?).

That leaves me with the "minimize" strategy.

 

I've done some amount of investigation and VI profiling and playing with the "in-place" structure to see if I can help things along.

 

For example, I have a few places where I have to transpoe some 2D arrays. . If I use the "show buffer allocations tool" the attaced screen-shot would indicate that I'm better off not using the IPE structure, both for the array transpose operation and for the index element operations? Since this seems counter-intutive to me, I must have some fundamental missunderstanding either with the "show buffer" tool or with IPE's or both.. The tool shows that a buffer is allocated going into the IPE and again going out of the IPE, and the transpose 2D array has an allocation in and out, even inside the IPE resulting in twice as many allocations as not using IPE.

 

For the indexing part, using IPE seems to result in 1.5 times as many allocations (not counting the fact that I have to individually wire the index numbers vs. letting LabVIEW auto-index from 0 on the non-IPE version).

 

The shown example has string conversions (not good from a mem alloc/dealloc point of view since LabVIEW cannot readily determine the "array" length of the string), but I have other sections of code that do a lot of the same type of stuff, but keeping it numerical throughout. 

 

If someone could help me understand why IPE seems to increase memory activity instead of decreasing it, I would be grateful. 

(PS> the 2D array is used in the "incoming" orientation by the rest of the code, so constructing the in-data array to avoid the transpose doesn't seem to be useful either.)

QFang
-------------
CLD LabVIEW 7.1 to 2016
0 Kudos
Message 1 of 5
(5,458 Views)

You have a misunderstanding about IPEs (and so do lots of other people, based on the questions posted here about them). They aren't helping you here, and wrapping them around code does not magically make that code use less memory. They only reduce memory when you know that the code inside of them can operate in-place and you want to provide that information to the compiler, although often the compiler can figure this out on its own.

 

None of the operations you're doing here can happen in-place - that is, they cannot recycle the input memory location to store the output. In general if an operation can operate in-place, it will, without the need for an IPE. If the Transpose Array function could operate in-place it would do so regardless of whether there's an IPE around it.

 

You are also misunderstanding the meaning of the buffer allocation dots. They indicate that a buffer MAY be allocated, not that one WILL be allocated. For example, the dots that you see on the index array are there because if the input array does not contain enough elements, LabVIEW will need to allocate memory so that those values will exist. However, if the input array does contain enough elements, then no additional memory is needed. (Also, the amount of memory used by one floating point value is so trivial that it's probably not worth trying to optimize, even on a cRIO).

 

I suspect that you're seeing a buffer allocation for the array at the IPE border because in that case the IPE is acting like an "Always Copy" function - you're telling the compiler "I need a dedicated copy of this array so I can operate on it." I'm not completely sure on that point, though - there might be some other good explanation.

Message 2 of 5
(5,445 Views)

Thanks for clearing some of that up for me!

 

For the index out of array in the IPE I was not trying to avoid duplication / memory copy of the individual elements, but of the array at large. As such, my posted example was very poor since the array I'm indexing out of stops with the index out function. I have other places where I continue to operate on the array of data after indexing out some of the elements; but on closer inspection of these cases, and with the new understanding of IPE's I determined that the compiler already is not duplicating the data on the wire at those branch-points.  

 

-My reasoning (though it was flawed) was to tell the compiler that "I do not need an extra copy of these arrays, I'm just going to take out some of the values".. Since a fork in a wire is a pretty easy way to increase the chance of data duplicates, I figured that the IPE index function, by nature of removing the need to split, or fork, the array wire (it has an in and an out), I would avoid duplication or have a better chance of avoiding duplication.  Its "hard" to trust the compiler too.. though the buffer "may" allocation tool is useful.

 

After having looked at several more cases (as I'm writing this post), I can't find any array operation that I'm doing in my code that reduces the black dots by including an IPE.. As such, I must STILL not understand IPE's correctly, because my conclusion at the moment is that you "never" haver to use them. Replace a subset in an array? no need to use them (in my code). Indexing out elements? no problem. . 

 

Also, its not so much doing these things once or twice, but when you do these operations hundreds of thousands of times on a controller that needs to have near 100% uptime in a completely unmanned and hard to reach location, you really want to keep the memory footprint as static as possible.. While this application isn't quite "NASA flight hardware" level (no safety concerns for example), I'm still trying to adopt as much as I can from coding standards of such organizations, which includes "all memory allocations must be static and allocated at compile time", "no dynamic pointers", etc. most of these I can only approximate due to my choice of using LabVIEW, but that is the reason I'm trying to avoid duplicating data (over and over and over in the span of months) as much as I can.

 

Thanks again, and hope for another comment or two, on IPE's, and on clever ways to reduce/prevent dynamic memory alloc and de-alloc's.. particularily with strings.. My only idea is to determine "max" string lengths for all cases, allocate a U8 array of that size and use FG's or similar to re-use the same U8 array(s) for all string operations... but I haven't done this yet as it would be pretty beastly of a task and some places you still can't avoid going from or to a string, e.g. file (and path) names that are dynamic with time/date at some point need to not be U8 arrays.. of course at this point I'm derailing my own thread.

-Q

QFang
-------------
CLD LabVIEW 7.1 to 2016
0 Kudos
Message 3 of 5
(5,415 Views)
Solution
Accepted by topic author QFang

QFang wrote:

-My reasoning (though it was flawed) was to tell the compiler that "I do not need an extra copy of these arrays, I'm just going to take out some of the values".. Since a fork in a wire is a pretty easy way to increase the chance of data duplicates, I figured that the IPE index function, by nature of removing the need to split, or fork, the array wire (it has an in and an out), I would avoid duplication or have a better chance of avoiding duplication.


It's important to realize that buffer allocations only occur at nodes, not on wires. While it may seem like forking a wire makes a copy of the data, that's not the case. At most the fork will cause a reference counter to increment. LabVIEW memory allocation is copy-on-write - no copy is made until data is actually modified, and even then, the copy is made only if there's a need to maintain the original. If you fork an array to several index array functions, there's still only one copy of the array. Also, the LabVIEW compiler tries to schedule operations to avoid copies, so if several branches read from a wire but only one modifies it, the compiler will try to schedule the modifying operation to run after all the reads are complete.


QFang wrote:

After having looked at several more cases (as I'm writing this post), I can't find any array operation that I'm doing in my code that reduces the black dots by including an IPE.. As such, I must STILL not understand IPE's correctly, because my conclusion at the moment is that you "never" haver to use them. Replace a subset in an array? no need to use them (in my code). Indexing out elements? no problem. .


An IPE is useful for replacing an array subset when you're operating on a subset of the original array. You pull out the elements you want, do some calculations, and then put them back into the same place in the array. If the new array subset comes from somewhere other than the original array, then the IPE doesn't help. If the input and output sides of the IPE don't connect to each other, then there's no benefit to the IPE.

 

I'm attaching an image of some code I wrote recently that makes use of IPEs, with buffer allocations shown. You can see that there's only one set of buffer allocations, after the Split 1D Array. I could have worked around that but the way I wrote it seemed easier, and the arrays are small and it's not time-critical code so it doesn't need any optimization. Also those arrays are always the same size, so it should be able to reuse the same allocation on every iteration of the VI rather than allocating new arrays.

IPE.PNG

 

Another important point: buffers can be reused. You might see an allocation dot on a shift register, but that shift register only needs to be allocated once, on the first call to the VI. Every following call to the VI reuses that same location. Sometimes you don't see a buffer allocation even though one effectively occurs. Resizing an array could requiring copying the entire array to a new larger location, and even though LabVIEW needs to allocate more memory for it, you won't always see a buffer allocation dot. I think this is because it's technically reallocating an existing array instead of allocating a new one, but this has always puzzled me a bit. On the topic of arrays, there are also times where you see a buffer allocation dot but all that's being allocated is a "sub-array" - a pointer to a specific part of an existing array, not an entirely new copy of the data. For example Reverse 1D Array can create a sub-array that points to the end of the original array with a "stride" of -1, meaning it advances through the array backwards. Same thing with array subset. You can see these sub-arrays by turning on context help and putting the cursor over a wire that carries one, as shown in this image.

subarray.PNG

 

There's unfortunately not much you can do about string allocations. Fortunately, I've never seen that be a problem, and I've had systems run continuously for months that used strings on limited hardware (the Compact FieldPoint controllers) for both logging to disk and TCP communication. I would recommend moving the string operations out of time-critical areas and into separate loop. For example, I put my TCP communication in a separate loop that also parses the incoming strings into specific data, which is then passed by queue (or RT-FIFO) to the time-critical loops so that those loops only deal with fixed-size data. Same idea with the logging - do all the string conversions and path handling in a separate loop.

Message 4 of 5
(5,402 Views)

Thank you Nathand for your your time and your insight into this topic!

 

I admittedly started a new thread that is closely related, but more generic, if that makes sense. In that thread I will continue with more examples, more in the format of an interactive journal really as I go through and try to think of creative ways to reduce calls to the memory manager. That thread can be found here: http://forums.ni.com/t5/LabVIEW/avoiding-data-memory-duplication-in-subVI-calls/td-p/2315880

QFang
-------------
CLD LabVIEW 7.1 to 2016
0 Kudos
Message 5 of 5
(5,394 Views)