LabVIEW Idea Exchange

cancel
Showing results for 
Search instead for 
Did you mean: 
0 Kudos
zenthoef

Arrays and Strings in Loops - Making it Computer Friendly

Status: Declined

Any idea that has not received any kudos within a year after posting will be automatically declined. 

VI Analyzer complains if you use the Build Array function or Concatenate Strings function in a loop because these functions negatively affect memory and processor usage in a loop. Using both of these functions in loops bring certain advantages to code. Primarily, you can dynamically build strings or arrays, which is helpful because sometimes it is hard (if not impossible) to know how big a string or array is going to be when a loop is run.

 

The first image below shows a loop with both of these functions that will cause a memory leak as the loop is run.

 Build Array and Con String.PNG

Build Array and Concatenate String in Loop - Will Cause Memory Leak

 

I got to wondering... Is there a way to dynamically build arrays and strings in a loop that will not cause a memory leak? In my limited testing, it looks like the code in the second image performs the same function as the build array and concatenate strings functions, but without the poor memory performance. I can run the code below for long periods of time and not have a memory leak. The instant I switch over to the case to use Build Array and Concatenate Strings the code beings to chew up memory. My idea is this: Replace the internals of the Build Array and Concatenate Strings functions with the code boxed in red in the image below. It is preferable that the code below gets contained in its own function because it clutters up any diagram with the extra nodes. 

 

If putting this code into the Build Array and Concatenate Strings functions is technically possible, it will allow people to dynamically build arrays and strings in loops without the costly memory performance. One note is that the code that builds an array in the second image is scalable, but the code that builds a string is not scalable. If this is implemented, the code to build a string should be scalable, but based on the code below that does not cause a memory leak.

Build Array and Con String - No Memory Leak.PNG

Replace Build Array and Concatenate Strings with the Code Boxed in Red

 

10 Comments
AristosQueue (NI)
NI Employee (retired)

> The first image below shows a loop with both of these functions that will cause a memory leak as the loop is run.

 

What? Sorry, your comment makes no sense. What memory leak? Memory is allocated, yes, but there is no leak.

 

As for the code in red, it is even less efficient than the nodes you are proposing replacing.

 

> If putting this code into the Build Array and Concatenate Strings functions is technically

> possible, it will allow people to dynamically build arrays and strings in loops without the

> costly memory performance.

 

No, it won't. That string or array still has to take on a different size on each iteration of the loop. You still have a reallocation each time through the loop. That's the thing you want to eliminate.

 

It often is impossible to eliminate this hit. That's why it is a warning from VI Analyzer, not a compiler error. Sometimes, you just have to live with the hit. But sometimes you can redesign your code entirely to avoid it. For example, if you knew that every string you were going to append was 1 character long, then you could preallocate and use the Replace Element code that you proposed. But unless you can fix the initial allocation problem, you're going to have the memory inefficiency.

 

But you still don't have a memory leak.

zenthoef
Active Participant

When I run the code with the Build Array and Concatenate Strings functions and monitor the amount of memory LabVIEW is using according to Windows Task Manager is tremendous. In a matter of minutes LabVIEW's memory usage becomes high enough that LabVIEW pops up a couple of messages. One of those popups reads, "Not Enough Memory to Complete Operation". Also, that memory is not released until I close the VI. Isn't that what a memory leak is? Using memory and not releasing it back?

 

I was under the impression that the tremendous memory usage is due to the reallocation each time through, but that it allocated a new copy in memory each time, independent of the old copy and that's where the inefficiency was. Maybe that impression was wrong. Even if my impression of what was going on was wrong, I still don't understand why the Build Array and Concatenate Strings functions use up a lot of memory and the boxed red code does not. What causes that difference? I see that difference both in Windows Task Manager and in "Tools->Profile->Performance and Memory...".

 

Even if it is less efficient, that seems better than having large amounts of memory consumed.

GregR
Active Participant

In a sense you're both right. You're just not talking about the same thing. The original diagram does not "leak" memory. It uses an ever increasing amount of memory. These are not technically the same thing, but they have the same result.

 

The code in the first loop adds an element to the array and concatenates more elements to the string each time that frame executes. Without giving these operations some kind of bound, they will consume all memory available to the process.

 

The code in the second loop always has an empty array (because replace array element will never grow the input array and the shift register starts with an empty array). I'm not even sure what the behavior of replace string subset is in this case.

 

I think what you're looking for is some sort of string and array with maximum sizes. There are many ways to handle what happens when you reach that maximum size though, so you need to specify what you want. In any case that would not be something we would add directly to build array and concatenate strings. If we did something, we would introduce some new concept.

altenbach
Knight of NI

OK, one problem with all this is that your "experiment" fails to follow all good scientific principles: You do two things in parallel (string+numeric) and you change both methods at the same time. In order to fully determine what is going on, you should either run the numeric and string experiments separately, or you would need to do at least four independent experiments with all possible combinations (upper numeric+upper string, lower numeric+upper string, upper numeric+lower string, lower numeric+lower string).

 

GregR wrote: The code in the second loop always has an empty array (because replace array element will never grow the input array and the shift register starts with an empty array).

 

Wait! Look again! This is an "insert into array, not a "replace array subset". (The icon goes from four squares to six squares!). The array grows the same way as in the first code unless he tries to accidentally insert at an out of range location.

 

I agree, this is not a memory leak, but just high memory consumption and memory thrashing, compounded by the fact that we are doing constant reallocations due to array resizing and thus causing memory fragmentation. I don't know what happens under the hood, but maybe the "insert into array" preallocates in larger chunks, thus causing a subtle difference. Most likely the differences you see are random and mostly depend in what order you do the tests. No matter how you do this, you will run out of memory eventually.

 

The solution is to pick an upper array size bounds, allocate it once (e.g. filled with NaN), and use "replace array subset" to substitute real data without resizing the array.

 

All this does not belong into the ideas exchange. Please start a new thread in the LabVIEW forum, attach your test VIs, and let's find out what is really going on!  Don't forget to attach your actual VIs so we can play around.

 

There is also the problem that you have no indicators. It is well possible that some of the code gets deadstripped under some conditions, giving a false benchmarking result.

altenbach
Knight of NI

OK, the reason your upper code consumes more memory is because the loop rate is much higher because the code is more efficient. Both consume the same amount of memory and will run out of memory eventually, the upper code just gets there much faster! 😄

AristosQueue (NI)
NI Employee (retired)

> OK, the reason your upper code consumes more memory is because the loop rate

> is much higher because the code is more efficient. Both consume the same amount

> of memory and will run out of memory eventually, the upper code just gets there

> much faster! 😄

 

Yes. Altenbach's statement agrees with my findings.

altenbach
Knight of NI

Yes, the main difference in is "built array" vs "insert into array". Here both are basically doing the same thing. I wasn't aware that "insert into array" is that much slower! Good to know!

 

I wonder why. Maybe built array does fewer, but bigger preemptive allocations whereas "insert" does a new allocation with every step. Do you have any details?

GregFreeman
Trusted Enthusiast

Altenbach-

 

I may be wrong here but it probably has to do with the "shifting" of the array when using insert into array, while build array just has to reallocate more memory to append. Obviously I'm not sure how the insert into array algorithm works in the background, but it wouldn't surprise me if it has no built in logic to determine how to allocate memory differently depending on the position of the insert. Like you said, often it is common when allocating memory dynamically that you don't allocate just what you need but instead allocate maybe double the size, then free up any extra memory after the dynamic build is done, rather than allocating only enough memory for 1 more element on each loop iteration. Insert in array may not allow for this because that would result in a bunch of null pointers in the middle of your array (I think). That said, I didn't run the code so that I'm not sure what "slower" is in this case. Does it slow down more and more as the array gets larger and larger? It would be interesting to see the difference in time between each loop iteration. I may be of base here but this is imo.

altenbach
Knight of NI

Is starts out about 25x slower, but as the size increases, it gets 100+ times slower.

 

In LabVIEW, arrays are always contiguous in memory, so resizing typically requires a new copy. Appending in a loop might preemptively allocate in larger junks, so it does not need to be done with each iteration.

Darren
Proven Zealot
Status changed to: Declined

Any idea that has not received any kudos within a year after posting will be automatically declined.